408重要数据结构+算法汇总——C语言手搓版(全)

该套代码,大学期间跟着网课一遍一遍打下来的,408大概就这些了,别的杂七杂八其实还有很多,遗憾的是,一直没有整理和归纳。导致一遍遍地学一遍遍地忘记。大四就快毕业了,研也考了。这里做个整理,算是给408一个小小的胶带吧。后续如果有缺漏还会弥补。

目录:

  1. malloc函数:malloc用于分配指定大小的内存块,并返回一个指向该内存块的指针。它只分配内存,不进行初始化。如果分配成功,则返回指向分配内存的指针;如果分配失败,则返回NULL。

  2. calloc函数:calloc用于分配指定数量和大小的连续内存块,并返回一个指向该内存块的指针。与malloc不同的是,calloc会将分配的内存块进行初始化,将每个字节都设置为0。如果分配成功,则返回指向分配内存的指针;如果分配失败,则返回NULL。

  3. realloc函数:realloc用于重新分配已经分配的内存块的大小。它接受两个参数,第一个参数是一个已经分配内存的指针,第二个参数是需要重新分配的大小。如果分配成功,则返回指向重新分配内存的指针;如果分配失败,则返回NULL。需要注意的是,如果realloc分配的新内存大小比原来的内存大小大,那么新分配的内存区域中的内容将被清除。

malloc、calloc、realloc都是C语言中用于动态内存分配的函数。它们之间的区别如下:

  • malloc只分配内存,不进行初始化。calloc会将分配的内存块进行初始化,将每个字节都设置为0。
  • malloc分配的内存块是未初始化的,因此在使用前需要进行初始化。calloc分配的内存块是初始化的,因此在使用前不需要进行初始化。
  • malloc分配的内存块可以是任意大小的。calloc分配的内存块必须是连续的,并且大小必须是2的幂次方。
  • malloc分配的内存块是不可变的,不能修改其大小。calloc分配的内存块是可变的,可以通过realloc函数修改其大小。

malloc、calloc、realloc的用法如下:

  • malloc的用法如下:
c 复制代码
#include <stdlib.h>

int* ptr;
ptr = (int*)malloc(10 * sizeof(int)); // 分配了10个int类型的内存空间
if (ptr == NULL) {
    // 内存分配失败的处理
} else {
    // 内存分配成功的处理
    // 使用ptr指针访问分配的内存空间
    // ...
    free(ptr); // 释放内存空间
}
  • calloc的用法如下:
c 复制代码
#include <stdlib.h>

int* ptr;
ptr = (int*)calloc(10, sizeof(int)); // 分配了10个int类型的内存空间,并初始化为0
if (ptr == NULL) {
    // 内存分配失败的处理
} else {
    // 内存分配成功的处理
    // 使用ptr指针访问分配的内存空间
    // ...
    free(ptr); // 释放内存空间
}
  • realloc的用法如下:
c 复制代码
#include <stdlib.h>

int* ptr;
ptr = (int*)malloc(10 * sizeof(int)); // 分配了10个int类型的内存空间
if (ptr == NULL) {
    // 内存分配失败的处理
} else {
    // 内存分配成功的处理
    // 使用ptr指针访问分配的内存空间
    // ...
    ptr = (int*)realloc(ptr, 20 * sizeof(int)); // 重新分配20个int类型的内存空间
    if (ptr == NULL) {
        // 内存分配失败的处理
    } else {
        // 内存分配成功的处理
        // 使用ptr指针访问分配的内存空间
        // ...
    }
}

线性表

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

typedef struct Vector {
    int *data;
    int size;
    int len;
} Vec;

Vec *init(int n) {
    Vec *v = (Vec *)malloc(sizeof(Vec));
    v->data = (int *)malloc(sizeof(int) * n);
    v->size = n;
    v->len = 0;
    printf("init Vector successfully!\n");
    return v;
}

int expand(Vec *v) {
    if (!v) {
        return 0;
    }
    int expsize = v->size;
    int *tmp;
    while (expsize) {
        tmp = (int *)realloc(v->data, sizeof(int) * (v->size + expsize));
        if (tmp) break;
        expsize >>= 1;
    }
    if (!tmp) {
        printf("expand failed~\n");
        return 0;
    }
    v->data = tmp;
    v->size += expsize;
    printf("expand successfully~\n");
    return 1;
}

int insert(Vec *v, int idx, int val) {
    if (!v) {
        return 0;
    }
    if (idx < 0 || idx > v->len) {
        return 0;
    }
    if (v->len == v->size) {
        if (!expand(v)) return 0;
    }
    memcpy(v->data + idx + 1, v->data + idx, sizeof(int) * (v->len - idx)); 
    v->data[idx] = val;
    v->len++;
    return 1;
}

int erase(Vec *v, int idx) {
    if (!v) {
        return 0;
    }
    if (idx < 0 || idx > v->len) {
        return 0;
    }
    memcpy(v->data + idx, v->data + idx + 1, sizeof(int) * (v->len - idx - 1)); 
    v->len--;
    return 1;
} 

void freeVec(Vec *v) {
    if (v) {
        free(v->data);
        free(v);
    }
    printf("free Vector successfully!\n");
    return ;
}

void showVec(Vec *v) {
    if (!v) return ;
    printf("Vec:[");
    for (int i = 0; i < v->len; i++) {
        i && printf(", ");
        printf("%d", v->data[i]);
    }
    printf("]\n");
    return ;
}

int main () {
    Vec *v = init(2);
    srand(time(0));
    int cnt = 20;
    while (cnt--) {
        int val = rand() % 100;
        int op = rand() % 4;
        int idx = rand() % (v->len + 3) - 1;
        switch (op) {
            case 0 : 
            case 1 : 
            case 2 : 
                printf("insert %d at %d, res = %d\n", val, idx, insert(v, idx, val));
                break;
            case 3 :
                printf("erase at %d, res = %d\n", idx, erase(v, idx));
                break;

        }
    }
    showVec(v);
    freeVec(v);
    return 0;
}

链表

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
typedef struct Node {
    int val;
    struct Node *next, *prev;
} Node;
typedef struct List {
    Node head;
    int len;
} List;
Node *initNode(int val) {
    Node *n = (Node *)malloc(sizeof(Node));
    n->val = val;
    n->next = n->prev = NULL;
    return n;
}
void freeNode(Node *n) {
    if (n) {
        free(n);
    }
    return ;
}
List *initList() {
    List *l = (List *)malloc(sizeof(List));
    l->len = 0;
    l->head.next = NULL;
    l->head.prev = NULL;
    return l;
}
void freeList(List *l) {
    if (!l) return ;
    Node *p = &(l->head), *k;
    while (p) {
        k = p;
        p = p->next;
        freeNode(k);
    }
    free(l);
    return ;
}
int insertNode(List *l, int idx, Node *n) {
    if (!l) return 0;
    if (idx < 0 || idx > l->len) return 0;
    Node *p = &(l->head);
    while (idx--) {
        p = p->next;
    }
    n->next = p->next;
    p->next = n;
    n->prev = p;
    if (n->next) {
        n->next->prev = n;
    }
    l->len++;
    return 1;
}
int insertVal(List *l, int idx, int val) {
    Node *n = initNode(val);
    if (!insertNode(l, idx, n)) {
        freeNode(n);
        return 0;
    }
    return 1;
}
int erase(List *l, int idx) {
    if (!l) return 0;
    if (idx < 0 || idx > l->len) return 0;
    Node *p = &(l->head);
    while (idx--) {
        p = p->next;
    }
    Node *k = p->next;
    p->next = k->next;
    if (k->next) {
        k->next->prev = p;
    }
    freeNode(k);
    l->len--;
    return 1;
}
int reverse(List *l) {
    if (!l) return 0;
    Node *p = l->head.next;
    Node *k;
    l->head.next = NULL;
    l->len = 0;
    while (p) {
        k = p;
        p = p->next;
        insertNode(l, 0, k);
    }
    return 1;
}
void showList(List *l) {
    if (!l) return ;
    Node *p = l->head.next;
    Node *k = &(l->head);
    printf("List+:[");
    while (p) {
        k = p;
        printf("%d->", p->val);
        p = p->next;
    }
    printf("NULL]\n");
    printf("List-:[");
    while (k != &(l->head)) {
        printf("%d->", k->val);
        k = k->prev;
    }
    printf("HEAD]\n");
    return ;
}
int main() {
    srand(time(0));
    int cnt = 20;
    List *l = initList();
    while (cnt--) {
        int val = rand() % 100;
        int opt = rand() % 5;
        int idx = rand() % (l->len + 3) - 1;
        switch (opt) {
            case 0:
            case 1:
            case 2:
                printf("insert %d at %d, res = %s\n", val, idx, insertVal(l, idx, val) ? "SUC" : "ERR");
                break;
            case 3:
                printf("erase at %d, res = %s\n", idx, erase(l, idx) ? "SUC" : "ERR");
                break;
            case 4:
                printf("reverse, res = %s\n", reverse(l) ? "SUC" : "ERR");
                break;
        }
        showList(l);
    }
    freeList(l);
    return 0;
}

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
typedef struct Stack {
    int *data;
    int top;
    int size;
} Stack;
Stack *initStack(int n) {
    Stack *s = (Stack *)malloc(sizeof(Stack));
    s->data = (int *)malloc(sizeof(int) * n);
    s->top = -1;
    s->size = n;
    return s;
}
void freeStack(Stack *s) {
    if (!s) return ;
    free(s->data);
    free(s);
    return ;
}
int expand(Stack *s) {
    if (!s) return 0;
    int expsize = s->size;
    int *tmp;
    while (expsize) {
        tmp = (int *)realloc(s->data, sizeof(int) * (s->size + expsize));
        if (tmp) break;
        expsize >>= 1;
    }
    if (!tmp) return 0;
    s->data = tmp;
    s->size += expsize;
    printf("expand successfully~, new size is %d\n", s->size);
    return 1;
}
int push(Stack *s, int val) {
    if (!s) return 0;
    if (s->top == s->size - 1) {
        if (!expand(s)) return 0;
    }
    s->data[++s->top] = val;
    return 1;
}
int isEmpty(Stack *s) {
    return !(s || s->top != -1);
}
int pop(Stack *s) {
    return s->data[s->top--];
}
void showStack(Stack *s) {
    if (isEmpty(s)) return ;
    printf("Stack:[");
    for (int i = 0; i <= s->top; i++) {
        i && printf(",");
        printf("%d", s->data[i]);
    }
    printf("]\n");
    return ;
}
int main() {
    srand(time(0));
    Stack *s = initStack(2);
    int cnt = 20;
    while (cnt--) {
        int val = rand() % 100;
        int opt = rand() % 4;
        switch (opt) {
            case 0:
            case 1:
            case 2:
                printf("push %d, res = %s.\n", val, push(s, val) ? "SUC" : "ERR");
                break;
            case 3:
                isEmpty(s) ? printf("pop nothing~\n") : printf("pop %d\n", pop(s));
                break;
        }
        showStack(s);
    }
    freeStack(s);
    return 0;
}

队列

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct Queue {
    int *data;
    int size;
    int head, tail;
} Queue;
Queue *initQueue(int n) {
    Queue *q = (Queue *)malloc(sizeof(Queue));
    q->data = (int *)malloc(sizeof(int) * n);
    q->size = n;
    q->head = q->tail = 0;
    return q;
}
void freeQueue(Queue *q) {
    if (!q) return ;
    free(q->data);
    free(q);
    return ;
}
int expand(Queue *q) {
    int expsize = q->size;
    int *tmp;
    while (expsize) {
        tmp = (int *)malloc(sizeof(int) * (q->size + expsize));
        if (tmp) break;
        expsize >>= 1;
    }
    if (!tmp) return 0;
    int i, j;
    for (i = q->head, j = 0; i != q->tail; i = (i + 1) % q->size, j++) {
        tmp[j] = q->data[i];
    }
    free(q->data);
    q->data = tmp;
    q->head = 0;
    q->tail = j;
    q->size += expsize;
    printf("expand successfully~, new size is %d\n", q->size);
    return 1;
}
int push(Queue *q, int val) {
    if (!q) return 0;
    if ((q->tail + 1) % q->size == q->head) {
        if (expand(q) < 0) {
            return 0;
        }
    }
    q->data[q->tail] = val;
    q->tail = (q->tail + 1) % q->size;
    return 1;
}
int isEmpty(Queue *q) {
    return !(q && (q->tail != q->head));
}
int pop(Queue *q) {
    int tmp = q->data[q->head];
    q->head = (q->head + 1) % q->size;      // 头节点是空的,尾结点是实的
    return tmp;
}
void showQueue(Queue *q) {
    if (isEmpty(q)) {
        return ;
    }
    printf("Queue:[");
    int i = q->head;
    for (; i != q->tail; i = (i + 1) % q->size) {
        i != q->head && printf(", ");
        printf("%d", q->data[i]);
    }
    printf("]\n");
    return ;
}
int main() {
    srand(time(0));
    Queue *q = initQueue(2);
    int cnt = 20;
    while (cnt--) {
        int val = rand() % 100;
        int opt = rand() % 4;
        switch (opt) {
            case 0:
            case 1:
            case 2:
                printf("push %d, res = %s.\n", val, push(q, val) ? "SUC" : "ERR");
                break;
            case 3:
                isEmpty(q) ? printf("pop nothing~\n") : printf("pop %d\n", pop(q));
                break;
        }
        showQueue(q);
    }
    freeQueue(q);
    return 0;
}

链栈

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
typedef struct Node {
    int val;
    struct Node *next;
} Node;
typedef struct ListStack {
    Node *top;
    int len;
} Stack;
Node *initNode(int val) {
    Node *n = (Node *)malloc(sizeof(Node));
    n->val = val;
    n->next = NULL;
    return n;
}
Stack *initStack() {
    Stack *s = (Stack *)malloc(sizeof(Stack));
    s->top = NULL;
    s->len = 0;
    return s;
}
void freeNode(Node *k) {
    if (k) {
        free(k);
    }
    return ;
}
void freeStack(Stack *s) {
    if (!s) return ;
    Node *p = s->top;
    Node *k;
    while (p) {
        k = p;
        p = p->next;
        freeNode(k);
    }
    free(s);
    return ;
}
int push(Stack *s, int val) {
    if (!s) return 0;
    Node *n = initNode(val);
    if (!n) return 0;
    n->next = s->top;
    s->top = n;
    s->len++;
    return 1;
}
int isEmpty(Stack *s) {
    return !(s && s->top);
}
int pop(Stack *s) {
    Node *k = s->top;
    int tmp = k->val;
    s->top = k->next;
    freeNode(k);
    return tmp;
}
void showStack(Stack *s) {
    if (!s) return ;
    printf("Stack:[");
    Node *p = s->top;
    while (p) {
        printf("%d->", p->val);
        p = p->next;
    }
    printf("NULL]\n");
    return ;
}
int main() {
    srand(time(0));
    Stack *s = initStack();
    int cnt = 20;

    while (cnt--) {
        int val = rand() % 100;
        int opt = rand() % 4;
        switch (opt) {
            case 0:
            case 1:
            case 2:
                printf("push %d, res = %s.\n", val, push(s, val) ? "SUC" : "ERR");
                break;
            case 3:
                isEmpty(s) ? printf("pop nothing~\n") : printf("pop %d\n", pop(s));
                break;
        }
        showStack(s);
    }
    freeStack(s);
    return 0;
}

链队

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
typedef struct Node {
    int val;
    struct Node *next;
} Node;
typedef struct Queue {
    Node *head;
    Node *tail;
} Queue;
Node *initNode(int val) {
    Node *n = (Node *)malloc(sizeof(Node));
    n->val = val;
    n->next = NULL;
    return n;
}
void freeNode(Node *k) {
    if (k) {
        free(k);
    }
    return ;
}
Queue *initQueue() {
    Queue *q = (Queue *)malloc(sizeof(Queue));
    q->head = NULL;
    q->tail = NULL;
    return q;
}
void freeQueue(Queue *q) {
    if (!q) return ;
    Node *p = q->head, *k;
    while (p) {
        k = p;
        p = p->next;
        freeNode(k);
    }
    free(q);
    return ;
}
int push(Queue *q, int val) {
    if (!q) return 0;
    Node *n = initNode(val);
    if (!n) return 0;
    if (q->tail) {
        q->tail->next = n;
        q->tail = n;
    } else {
        q->head = n;
        q->tail = n;
    }
    return 1;
}
int isEmpty(Queue *q) {
    return !(q && q->head);
}
int pop(Queue *q) {
    Node *k = q->head;
    int tmp = k->val;
    q->head = k->next;
    freeNode(k);
    if (!q->head) q->tail = NULL;
    return tmp;
}
void showQueue(Queue *q) {
    if (!q) return ;
    printf("Queue:[");
    Node *p = q->head;
    while (p) {
        printf("%d->", p->val);
        p = p->next;
    }
    printf("NULL]\n");
    return ;
}
int main() {
    srand(time(0));
    Queue *q = initQueue();
    int cnt = 20;

    while (cnt--) {
        int val = rand() % 100;
        int opt = rand() % 4;
        switch (opt) {
            case 0:
            case 1:
            case 2:
                printf("push %d, res = %s.\n", val, push(q, val) ? "SUC" : "ERR");
                break;
            case 3:
                isEmpty(q) ? printf("pop nothing~\n") : printf("pop %d\n", pop(q));
                break;
        }
        showQueue(q);
    }
    freeQueue(q);
    return 0;
}

二叉树

c 复制代码
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
typedef struct Node {
    int val;
    struct Node *left;
    struct Node *right;
} Node;
typedef struct Tree {
    Node *root;
    int len;
} Tree;
Node *initNode(int val) {
    Node *n = (Node *)malloc(sizeof(Node));
    n->val = val;
    n->left = n->right = NULL;
    return n;
}
void freeNode(Node *p) {
    if (!p) return ;
    free(p);
    return ;
}
Tree *initTree() {
    Tree *t = (Tree *)malloc(sizeof(Tree));
    t->root = NULL;
    t->len = 0;
    return t;
}
Node *insert(Node *root, int val) {
    if (!root) {
        return initNode(val);
    }
    if (val > root->val) {
        root->right = insert(root->right, val);
    } else {
        root->left = insert(root->left, val);
    }
    return root;
}
void insertTree(Tree *t, int val) {
    if (!t) return ;
    t->root = insert(t->root, val);
    t->len++;
    return ;
}
void preorderTrav(Node *root) {
    if (!root) return ;
    printf("%d,", root->val);
    preorderTrav(root->left);
    preorderTrav(root->right);
    return ;
}
void preorderTree(Tree *t) {
    if (!t) return ;
    printf("Pre :[");
    preorderTrav(t->root);
    printf("]\n");
    return ;
}
void inorderTrav(Node *root) {
    if (!root) return ;
    inorderTrav(root->left);
    printf("%d,", root->val);
    inorderTrav(root->right);
    return ;
}
void inorderTree(Tree *t) {
    if (!t) return ;
    printf("In  :[");
    inorderTrav(t->root);
    printf("]\n");
    return ;
}
void postorderTrav(Node *root) {
    if (!root) return ;
    postorderTrav(root->left);
    postorderTrav(root->right);
    printf("%d,", root->val);
    return ;
}
void postorderTree(Tree *t) {
    if (!t) return ;
    printf("Post:[");
    postorderTrav(t->root);
    printf("]\n");
    return ;
}
void freeAll(Node *root) {
    if (!root) return ;
    freeAll(root->left);
    freeAll(root->right);
    freeNode(root);
    return ;
}
void freeTree(Tree *t) {
    if (!t) return ;
    freeAll(t->root);
    free(t);
    return ;
}
void outputTable(Node *root) {
    if (!root) return ;
    printf("%d", root->val);
    if (!root->left && !root->right) return ;
    printf("(");
    outputTable(root->left);
    printf(",");
    outputTable(root->right);
    printf(")");
    return ;
}
void outputTableTree(Tree *t) {		// 输出广义表
    if (!t) return ;
    printf("Table:[");
    outputTable(t->root);
    printf("]\n");
    return ;
}
Node *findNode(Node *root, int val) {
    if (!root) return NULL;
    if (val == root->val) return root;
    if (val > root->val) return findNode(root->right, val);
    else return findNode(root->left, val);
}
Node *findTree(Tree *t, int val) {
    if (!t) return NULL;
    return findNode(t->root, val);
}
typedef struct Stack {
    Node **data;
    int size;
    int top;
} Stack;
Stack *initStack(int n) {
    Stack *s = (Stack *)malloc(sizeof(Stack));
    s->data = (Node **)malloc(sizeof(Node *));
    s->top = -1;
    s->size = n;
    return s;
}
void freeStack(Stack *s) {
    if (!s) return ;
    free(s->data);
    free(s);
    return ;
} 
int push(Stack *s, Node *n) {
    if (!s) return 0;
    if (s->top == s->size - 1) return 0;
    s->data[++s->top] = n;
    return 1;
}
int isEmpty(Stack *s) {
    return !(s && s->top != -1);
}
Node *pop(Stack *s) {
    return s->data[s->top--];
}
Node *buildTree(char *str) {	// 广义表生成树
    Stack *s = initStack(strlen(str) / 2);
    Node *root, *n;
    int flag = 0;
    while (str[0]) {
        switch(str[0]) {
            case '(':
                push(s, n);
                flag = 0;   // 接下来插到左子树
                break;
            case ',':
                flag = 1;   // 接下来插到右子树
                break;
            case ')':
                root = pop(s);
                break;
            default:
                if (str[0] < '0' || str[0] > '9') break;
                int num = 0;
                while (str[0] >= '0' && str[0] <= '9') {
                    num = num * 10 + str[0] - '0';
                    str++; 
                }
                str--;
                n = initNode(num);
                if (!isEmpty(s)) {
                    flag ? (s->data[s->top]->right = n) : (s->data[s->top]->left = n);
                }
        }
        str++;
    }
    return root;
}
int main() {
    srand(time(0));
    Tree *t = initTree();
    int cnt = 10;
    int want = 0;
    while (cnt--) {
        int val = rand() % 100;
        insertTree(t, val);
        if (cnt == 9) want = val;
    }
    preorderTree(t);
    inorderTree(t);
    postorderTree(t);
    outputTableTree(t);
    Node *find = findTree(t, want);
    find ? printf("find %d at %#x, val = %d\n", want, find, find->val) : printf("%d not found~\n");
    freeTree(t);
    
	// 广义表生成树
    Tree *t1 = initTree();
    char str[100];
    scanf("%s", str);
    t1->root = buildTree(str);
    preorderTree(t1);
    outputTableTree(t1);
    freeTree(t1);
    return 0;
}

线索二叉树

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define CHILD 0
#define THREAD 1
typedef struct Node {
    int val;
    struct Node *left, *right;
    int ltag, rtag;
} Node;
Node *initNode(int val) {
    Node *n = (Node *)malloc(sizeof(Node));
    n->val = val;
    n->left = n->right = NULL;
    n->ltag = n->rtag = CHILD;
    return n;
}
void freeNode(Node *p) {
    if (!p) return ;
    free(p);
    return ;
}
Node *insert(Node *root, int val) {
    if (!root) return initNode(val);
    if (val > root->val) root->right = insert(root->right, val);
    else root->left = insert(root->left, val);
    return root;
}
void freeAll(Node *root) {
    if (!root) return ;
    if (root->ltag == CHILD) freeAll(root->left);
    if (root->rtag == CHILD) freeAll(root->right);
    freeNode(root);
    return ;
}
void inorderTrav(Node *root) {
    if (!root) return ;
    inorderTrav(root->left);
    printf("%d ", root->val);
    inorderTrav(root->right);
    return ;
}
void inorderTravThread(Node *root) {
    if (!root) return ;
    if (root->ltag == CHILD) inorderTravThread(root->left);
    printf("%d ", root->val);
    if (root->rtag == CHILD) inorderTravThread(root->right);
    return ;
}
Node *pre = NULL;
// 构建中序线索树
void buildThreadIn(Node *root) {
    if (!root) return ;
    // 先构建左子树的线索
    buildThreadIn(root->left);
    // 再构建根节点的线索
    if (!root->left) {  // 当前左指针为空,让左指针指向前驱
        root->left = pre;
        root->ltag = THREAD;
    }
    if (pre && !pre->right) {   // 前驱的右子树为空,前驱的右子树指向root
        pre->right = root;
        pre->rtag = THREAD;
    }
    pre = root;
    // 再构建右子树的线索
    buildThreadIn(root->right);
    return ;
}
// 构建后序线索树
void buildThreadPost(Node *root) {
    if (!root) return ;
    buildThreadPost(root->left);    // 递归,线索化左子树
    buildThreadPost(root->right);   // 递归,线索化右子树
    if (!root->left) {  
        root->left = pre;
        root->ltag = THREAD;
    }
    if (pre && !pre->right) {   
        pre->right = root;
        pre->rtag = THREAD;
    }
    pre = root;
    return ;
}
Node *getLeftMost(Node *p) {
    while (p && p->ltag == CHILD && p->left) p = p->left;
    return p;
}
// 非递归中序遍历输出线索树
void output(Node *root) {
    if (!root) return ;
    Node *p = getLeftMost(root);
    while (p) {
        printf("%d ", p->val); 
        if (p->rtag == CHILD) {
            p = getLeftMost(p->right);
        } else {
            p = p->right;
        }
    }
    return ;
}
int main() {
    srand(time(0));
    Node *root = NULL;
    int cnt = 10;
    while (cnt--) {
        int val = rand() % 100;
        root = insert(root, val);
        printf("%d ", val);
    }
    putchar(10);
    
    inorderTrav(root);
    putchar(10);
    
    buildThreadIn(root);
    output(root);
    putchar(10);
    
    inorderTravThread(root);
    putchar(10);

    // 后序线索树
    // pre = NULL;
    // buildThreadPost(root);
    // if (pre->right == NULL) pre->rtag = 1;


    freeAll(root);
    return 0;
}

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define swap(a, b) { \
    __typeof(a) __tmp = a; \
    a = b; b = __tmp; \
}

typedef struct Heap {
    int *data;
    int size;
    int len;
} Heap;

Heap *initHeap(int n) {
    Heap *h = (Heap *)malloc(sizeof(Heap));
    h->data = (int *)malloc(sizeof(int) * n);
    h->len = 0;
    h->size = n;
    return h;
}

void freeHeap(Heap *h) {
    if (!h) return ;
    free(h->data);
    free(h);
    return ;
}
int push(Heap *h, int val) {
    if (!h) return 0;
    if (h->size == h->len) {
        return 0;
    }
    h->data[h->len] = val;
    int idx = h->len;
    while (idx > 0 && h->data[idx] > h->data[(idx - 1) / 2]) {
        swap(h->data[idx], h->data[(idx - 1) / 2]);
        idx = (idx - 1) / 2;
    }
    h->len++;
    return 1;
}
void downAdj(int *arr, int idx, int len) {
    while (idx * 2 + 1 < len) {
        int tmp = idx, l = idx * 2 + 1, r = idx * 2 + 2;
        if (arr[l] > arr[tmp]) tmp = l;
        if (r < len && arr[r] > arr[tmp]) tmp = r;
        if (tmp == idx) break;
        swap(arr[tmp], arr[idx]);
        idx = tmp;        
    }
    return ;
}
int isEmpty(Heap *h) {
    return !h || h->len == 0;
}
int pop(Heap *h) {
    int ret = h->data[0];
    h->data[0] = h->data[h->len - 1];
    h->len--;    
    downAdj(h->data, 0, h->len);
    return ret;
}
void showHeap(Heap *h) {
    printf("Heap:[");
    int i;
    for (i = 0; i < h->len; i++) {
        i && printf(" ");
        printf("%d", h->data[i]);
    }
    printf("]\n");
}
int main() {
    srand(time(0));
    #define HEAPLEN 10
    Heap *h = initHeap(HEAPLEN);
    int cnt = HEAPLEN;
    while (cnt--) {
        int val = rand() % 100;
        push(h, val);
    }
    showHeap(h);
    printf("pop: [");
    while (!isEmpty(h)) {
        printf("%d ", pop(h));
    }
    printf("]\n");
    freeHeap(h);
    #undef HEAPLEN
    return 0;
}

堆排序

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define swap(a, b) { \
    __typeof(a) __tmp = a; \
    a = b; b = __tmp; \
}
void downAdj(int *arr, int idx, int len) {
    while (idx * 2 + 1 < len) {
        int tmp = idx, l = tmp * 2 + 1, r = tmp * 2 + 2;
        if (arr[l] > arr[tmp]) tmp = l;
        if (r < len && arr[r] > arr[tmp]) tmp = r;
        if (tmp == idx) break;
        swap(arr[tmp], arr[idx]);
        idx = tmp; 
    }
    return ;
}
void heap_sort(int *arr, int len) {
    int i;
    // 先把序列调整成一个堆
    for (i = (len - 2) / 2; i >= 0; i--) {
        downAdj(arr, i, len);
    }
    // 在堆的基础上再进行堆排序
    for (i = len - 1; i > 0; i--) {
        swap(arr[0], arr[i]);
        downAdj(arr, 0, i);
    }
    return ;
}
void showArr(int *arr, int len) {
    printf("arr:[");
    int i;
    for (i = 0; i < len; i++) {
        i && printf(" ");
        printf("%d", arr[i]);
    }
    printf("]\n");
}
int main() {
    srand(time(0));
    #define ARRLEN 10
    int arr[ARRLEN];
    int i;
    for (i = 0; i < ARRLEN; i++) {
        arr[i] = rand() % 100;
    }
    showArr(arr, ARRLEN);
    heap_sort(arr, ARRLEN);
    showArr(arr, ARRLEN);
    #undef ARRLEN

    return 0;
}

并查集(quickfind、quickunion、w-qickunion、路径压缩)

解决连通性问题。

  • quickFind算法:判通O(1);合并O(n)

算法思想:

  1. 基于染色思想,一开始每个人都是自己一个颜色
  2. 连接两个点时,把两个点设置成一个颜色。
  3. 如果两个点颜色一样,说明是一伙的,否则不是

编码设计:(C语言)

c 复制代码
#include<stdio.h>
#include<stdlib.h>

// 并查集的基础结构------color数组+size大小
typedef struct UnionSet {
    int *color; // 存储每个人的颜色
    int size; // 代表有多少个人
}UnionSet;

// 并查集的初始化
UnionSet *initSet(int n) {
    UnionSet *u = (UnionSet *)malloc(sizeof(UnionSet));
    u->color = (int *)malloc(sizeof(int) * n);
    u->size = n;
    for(int i=0;i<u->size;i++) u->color[i] = i; // 初始化每个人都是自己一个颜色
    return u;
}

// 内存的释放
void freeSet(UnionSet *u) {
    if(!u) return;
    free(u->color); // 释放顺序:先释放里面的零部件,后释放整个并查集
    free(u);
    return ;
}

// 通过下标寻找当前节点在并查集中的所属颜色。
int find(UnionSet *u, int idx){
    return u->color[idx];
}

// 完成合并操作
int merge(UnionSet *u,int a,int b) {
    if(find(u,a)==find(u,b)) return 0;
    int acolor = find(u,a); // 获取a的颜色
    int i;
    for(i=0;i<u->size;i++){ // 把所有是a的颜色的人改为b的颜色------将a合并到b中
        if(find(u,i) == acolor){
            u->color[i] = u->color[b];
        }
    }
    return 1;
}

int main(){
    int n,m; //  n为节点的个数, m代表边的个数。
    scanf("%d%d", &n,&m);
    UnionSet *u = initSet(n + 1);
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        switch(a){
            case 1: // 如果是1就合并b,c
                merge(u,b,c);
                break;
            case 2: // 如果是2,就输出b和c的关系
                printf("%s\n", find(u,b)==find(u,c)?"Yes":"No");
                break;
        }
    }
    freeSet(u);
    return 0;
}

C++版:

c 复制代码
#include<iostream>
#include<cstdio>
using namespace std;
class UnionSet {
public:
    UnionSet(int n) : color(new int[n]), size(n) {
        for (int i = 0; i < n; i++) color[i] = i;
    }
    int find(int ind) {
        return color[ind];
    }
    int merge(int a,int b) {
        int fa = find(a), fb = find(b);
        if(fa == fb) return 0;
        for(int i=0;i<size;i++){
            if(find(i) == fa) color[i] = fb;
        }
        return 1;
    }
    ~UnionSet() {
        delete[] color;
    }
    int *color;
    int size;
};

int main() {
    int n,m;
    cin >> n >> m;
    UnionSet u(n + 1);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        switch(a) {
            case 1: {
                u.merge(b, c);
                break;
            }
            case 2: {
                // 判断是否联通
                printf("%s\n", u.find(b) == u.find(c)?"Yes":"No");
                break;
            }
        }
    }
    return 0;
}

样例输入

6 5
1 1 2
2 1 3
1 2 4
1 4 3
2 1 3

样例输出

No
Yes
  • quickUnion算法
  1. 一开始每个人都是自己的一个队友,相当于没有队友。
  2. 连接你和你的队友时,让他没有队友的队友,作为你没有队友的队友,的队友
  3. 如果两个人,没有队友的队友,是一个人,说明他们是队友。
c 复制代码
class UnionSet {
public:
    UnionSet(int n) : father(new int[n]), size(n) {
        for(int i=0;i<n;i++){
            father[i]=i;
        }
    }
    int find(int ind) {
        if(father[ind] == ind) return ind;
        return find(father[ind]);
    }
    void merge(int a,int b) {
		int fa = find(a), fb = find(b);
        if(fa ==fb) return ;
        father[fa] = fb;
        return ;
    }
    int *father;
    int size;
    ~UnionSet(){
        delete[] father;
    }
};
  • weighted quickUnion
    如果一个帮派的人数大于另一个帮派的人数。那么当这两个帮派合并的时候,人数少的一方自然要认人数多的一方。weighted quickUnion算法也是如此。
c 复制代码
class UnionSet {
public:
    UnionSet(int n) : father(new int[n]), len(new int[n]), size(n) {
        for(int i=0;i<size;i++) {
            father[i]=i;
            len[i]=i;
        }
    }
    int find(int ind) {
        if(father[ind] == ind) return ind;
        return find(father[ind]);
    }
    int merge(int a,int b) {
        int fa=find(a), fb=find(b);
        if(fa==fb) return 0;
        if(len[fa]>len[fb]){
            father[fb]=fa;
            len[fa]+=len[fb];
        }else{
            father[fa]=fb;
            len[fb]+=len[fa];
        }
        return 1;
    }
    ~UnionSet() {
        delete[] len;
        delete[] father;
    }
    int *father, *len, size; // 增加一个变量len
};
  • 路径压缩
    在寻找父亲find()的路上,如果找到了最终的父亲,那么之前一路以来的所有孩子都可以直接认它为父节点。这样当再次查找的时候就可以简单很多。
c 复制代码
class UnionSet {
public:
    UnionSet(int n) : father(new int[n]), size(n) {
        for(int i=0;i<n;i++){
            father[i]=i;
        }
    }
    // 路径压缩核心代码:
    int find(int ind) {
		return father[ind] = father[ind]==ind?ind:find(father[ind]);
    }
    void merge(int a,int b) {
		int fa = find(a), fb = find(b);
        if(fa ==fb) return ;
        father[fa] = fb;
        return ;
    }
    int *father;
    int size;
    ~UnionSet(){
        delete[] father;
    }
};

AVL树

c 复制代码
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
    int key, h;
    struct Node *lchild, *rchild;
} Node;

Node __NIL;
#define NIL (&__NIL)

#define DEBUG // 也可以用gcc avl.c -D DEBUG 定义
#ifdef DEBUG
#define LOG(frm, args...) { \
    printf(frm, ##args); \
}
#else
#define LOG(frm, args...) {}
#endif

__attribute__((constructor))
void init_NIL() {
    NIL->key = NIL->h = 0;
    NIL->lchild = NIL->rchild = NIL;
    return ;
}

Node *getNewNode(int key) {
    Node *p = (Node *)malloc(sizeof(Node));
    p->key = key;
    p->h = 1;
    p->lchild = p->rchild = NIL;
    return p;
}
#define max(a, b) ((a) > (b) ? (a) : (b))
void update_hight(Node *root) {
    root->h = max(root->lchild->h, root->rchild->h) + 1;
    return ;
}
Node *left_rotate(Node *root) {
    LOG("%d left rotate\n", root->key);
    Node *new_root = root->rchild;
    root->rchild = new_root->lchild;
    new_root->lchild = root;
    update_hight(root);
    update_hight(new_root);
    return new_root;
}
Node *right_rotate(Node *root) {
    LOG("%d right rotate\n", root->key);
    Node *new_root = root->lchild;
    root->lchild = new_root->rchild;
    new_root->rchild = root;
    update_hight(root);
    update_hight(new_root);
    return new_root;
}
const char *type_str[4] = {"LL", "LR", "RR", "RL"};
Node *maintain(Node *root) {
    if (abs(root->lchild->h - root->rchild->h) < 2) return root;
    int type = 1;
    if (root->lchild->h > root->rchild->h) {
        if (root->lchild->lchild->h < root->lchild->rchild->h) {
            root->lchild = left_rotate(root->lchild);
            type += 1;
        }
        type += 1;
        root = right_rotate(root);
    } else {
        type = 1;
        if (root->rchild->rchild->h < root->rchild->lchild->h) {
            root->rchild = right_rotate(root->rchild);
            type += 1;
        }
        type += 1;
        root = left_rotate(root);
    }
    LOG("TYPE : %s\n", type_str[type]);
    return root;
}
Node *insert(Node *root, int key) {
    if (root == NIL) return getNewNode(key);
    if (root->key == key) return root;
    if (root->key > key) root->lchild = insert(root->lchild, key);
    else root->rchild = insert(root->rchild, key);
    update_hight(root);
    return maintain(root);
}
Node *erase(Node *root, int key) {
    if (root == NIL) return root;
    if (root->key > key) root->lchild = erase(root->lchild, key);
    else if (root->key < key) root->rchild = erase(root->rchild, key);
    else {
        if (root->lchild == NIL || root->rchild == NIL) {   // 同时符合 单孩子节点 和 叶节点 的删除情形
            Node *temp = root->lchild != NIL ? root->lchild : root->rchild;
            free(root);
            return temp;
        } else {
            Node *temp = root->lchild; // 找到前驱,然后交换,然后在左子树中删掉
            while (temp->rchild != NIL) temp = temp->rchild;
            root->key = temp->key;
            root->lchild = erase(root->lchild, temp->key);
        }
    }
    update_hight(root);
    return maintain(root);
}
void clear(Node *root) {
    if (root == NIL) return ;
    clear(root->lchild);
    clear(root->rchild);
    free(root);
    return ;
}
void print_node(Node *root) {
    printf("[ %d(%d) | %d, %d ]\n",
            root->key, root->h,
            root->lchild->key,
            root->rchild->key);
    return ;
}
void output(Node *root) {
    if (root == NIL) return ;
    print_node(root);
    output(root->lchild);
    output(root->rchild);
    return ;
}
int main () {
    Node *root = NIL;
    int key;
    // insert
    while (~scanf("%d", &key)) {
        if (key == -1) break;
        printf("insert %d to AVL Tree\n", key);
        root = insert(root, key);
        output(root);
    }
    // erase
    while (~scanf("%d", &key)) {
        if (key == -1) break;
        printf("erase %d from AVL Tree\n", key);
        root = erase(root, key);
        output(root);
    }
    
    return 0;
}
// 5 9 8 3 2 4 1 7 -1 4 -1

红黑树

红黑树 - 结构定义(五个条件):

  1. 每个节点非黑即红
  2. 根节点是黑
  3. 叶节点(NIL)是黑色(默认隐藏,画图的时候不用画出来)
  4. 如果一节点是红色,则它的两个子节点都是黑色的
  5. 从根节点出发到所有叶节点路径上,黑色节点数量相同

红黑树 - 学习诀窍

  1. 插入调整站在祖父节点
  2. 删除调整站在父节点
  3. 插入和删除的情况处理一共总结为五种
  4. 理解调整策略时,时刻记住黑色节点数量调整前后相同

插入调整:

叔叔节点是红色:直接红色上浮。

叔叔节点是黑色:利用平衡树的这一套思想。

  1. 判断类型,左旋右旋
  2. 红色上浮、红色下沉。

删除调整:

总共有以下几种情形:

  • 删除度为2,等价于删除度为0和度为1的节点
  • 不存在度为1的红色节点。
  • 删除度为1的黑色节点,他的孩子只有可能是红色
  • 删除度为0的黑色节点,产生双重黑色节点。
  • 删除度为0的红色节点,没啥好说的,就直接删。

如果兄弟节点是黑色,并且兄弟节点下面没有红色子节点

  • 老大+1重黑色,左右节点-1重黑色。

兄弟节点是黑色,兄弟节点的右子树(外八字)是红色,RR类型

  • 首先一个大左旋,两个子孩子改黑,新根改成原根的颜色。
  • 因为原根和父节点不存在颜色上的冲突,所以新根和父节点也不存在颜色上的冲突。

兄弟节点是黑色,兄弟节点的左子树(内八字)是红色,RL类型

  • 小右旋,新根和原根的节点互换
  • RR类型,大左旋即可。

兄弟节点是红色节点

  • 一个大左旋,把左子树(此处)改成红色。
  • 发现双重黑节点的兄弟节点是黑色,转化问题即可

测试输入:

78 65 45 36 56 72 90 100 32 7 4 15 17 -1
90 36 45 56 4 15 7 32 -1
c 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int key, color; // 0-red;1-black;2-double black
    Node *lchild, *rchild;
} Node;

Node __NIL;
#define NIL (&__NIL)

__attribute__((constructor))
void init_NIL() {
    NIL->key = 0;
    NIL->color = 1;
    NIL->lchild = NIL->rchild = NIL;
    return ;
}

Node *getNewNode(int key) {
    Node *p = (Node *)malloc(sizeof(Node));
    p->key = key;
    p->lchild = p->rchild = NIL;
    p->color = 0;
    return p;
}

int has_red_child(Node *root) {
    return root->lchild->color == 0 || root->rchild->color == 0;
}
Node *left_rotate(Node* root) {
    printf("left rotate : %d\n", root->key);
    Node *new_root = root->rchild;
    root->rchild = new_root->lchild;
    new_root->lchild = root;
    return new_root;
}
Node *right_rotate(Node* root) {
    printf("right rotate : %d\n", root->key);
    Node *new_root = root->lchild;
    root->lchild = new_root->rchild;
    new_root->rchild = root;
    return new_root;
}
const char *insert_maintain_type[] = {
    "1 : change color",
    "2 : LL",
    "2 : LR",
    "2 : RR",
    "2 : RL",
};
    
Node* insert_maintain(Node *root) {
    if (!has_red_child(root)) return root;
    if (!(root->lchild->color == 0 && has_red_child(root->lchild))
    && !(root->rchild->color == 0 && has_red_child(root->rchild))) return root; // 站在祖父节点,如果左孩子没有失衡,右孩子也没有失衡,就可以返回了
    // rotate
    int type = 0;
    if (root->rchild->color == 1) {
        if (root->lchild->rchild->color == 0) {
            root->lchild = left_rotate(root->lchild); // LR类型:需要首先进行一个小左旋
            type += 1;
        }
        type += 1;
        root = right_rotate(root);
    } else if (root->lchild->color == 1) {
        type = 2;
        if (root->rchild->lchild == 0) {
            type += 1;
            root->rchild = right_rotate(root->rchild);
        }
        type += 1;
        root = left_rotate(root);
    }
    printf("insert maintain type = %s\n", insert_maintain_type[type]);
    // 红色上浮
    root->color = 0;
    root->lchild->color = root->rchild->color = 1;
    return root;    
}

Node *__insert(Node *root, int key) {
    if (root == NIL) return getNewNode(key); // 如果是一棵空树,那么插入的节点应该是黑色,所以就需要额外封装一层
    if (root->key == key) return root;
    if (root->key > key) root->lchild = __insert(root->lchild, key);
    else root->rchild = __insert(root->rchild, key);
    return insert_maintain(root);
}

Node *insert(Node *root, int key) {
    root = __insert(root, key);
    root->color = 1; // 保证每次根节点都是黑色的
    return root;
}
const char *erase_maintain_type_str[] = {
    "red 1(right) : change color",
    "red 2(left)  : change color",
    "black 1 : no red child",
    "black 2 : LL",
    "black 2 : LR",
    "black 2 : RR",
    "black 2 : RL",
};
Node *erase_maintain(Node *root) {
    if (root->lchild->color != 2 && root->rchild->color != 2) return root;
    int type = 0;
    // 兄弟节点是红色
    if (has_red_child(root)) {
        root->color = 0;
        if (root->lchild->color == 0) root = right_rotate(root);
        else root = left_rotate(root), type = 1;
        root->color = 1;
        if (type) root->lchild = erase_maintain(root->lchild);
        else root->rchild = erase_maintain(root->rchild);
        printf("brother is %s\n", erase_maintain_type_str[type]);
        return root;
    }
    // 双黑节点的兄弟节点没有红色子孩子
    if ((root->rchild->color == 1 && !has_red_child(root->rchild)) 
    || (root->lchild->color == 1 && !has_red_child(root->lchild))) {
        type = 2;
        root->lchild->color -= 1;
        root->rchild->color -= 1;
        root->color += 1;
        printf("brother is %s\n", erase_maintain_type_str[type]);
        return root;
    }
    // 兄弟节点有红色子孩子
    type = 2;
    if (root->lchild->color == 1) {
        if (root->lchild->lchild->color != 0) {
            type += 1;
            root->lchild = left_rotate(root->lchild);
        }
        type += 1;
        root->lchild->color = root->color; // 新根节点颜色改为原根节点颜色
        root = right_rotate(root);
    } else {
        type = 3;
        if (root->rchild->rchild->color != 0) {
            type += 1;
            root->rchild = right_rotate(root->rchild);
        }
        type += 1;
        root->rchild->color = root->color; // 新根节点颜色改为原根节点颜色
        root = left_rotate(root);
    }
    root->lchild->color = root->rchild->color = 1;  // 左右子孩子的颜色都改为黑色
    printf("brother is %s\n", erase_maintain_type_str[type]);
    return root;
}

Node* __erase(Node *root, int key) {
    if (root == NIL) return root;
    if (root->key > key) root->lchild = __erase(root->lchild, key);
    else if (root->key < key) root->rchild = __erase(root->rchild, key);
    else {
        if (root->lchild == NIL || root->rchild == NIL) {
            Node *temp = root->lchild == NIL ? root->rchild : root->lchild;
            temp->color += root->color;            
            free(root);
            return temp;
        } else {
            Node *temp = root->lchild;
            while (temp->rchild != NIL) temp = temp->rchild;
            root->key = temp->key;
            root->lchild = __erase(root->lchild, temp->key);
        }
    }
    return erase_maintain(root);
}

Node *erase(Node *root, int key) {
    root = __erase(root, key);
    root->color = 1;
    return root;
}


void clear(Node *root) {
    if (root == NIL) return ;
    clear(root->lchild);
    clear(root->rchild);
    free(root);
    return ;
}
void print_node(Node *root) {
    printf(" (%d(%d) | %d, %d )\n", root->key, root->color,
    root->lchild->key, root->rchild->key);
    return ;
}
void output(Node *root) {
    if (root == NIL) return ;
    print_node(root);
    output(root->lchild);
    output(root->rchild);
    return ;
}
int main() {
    Node *root = NIL;
    int key;
    // insert
    while (~scanf("%d", &key)) {
        if (key == -1) break;
        printf("\n==== insert %d to red_black_tree ====\n", key);
        root = insert(root, key);
        output(root);
    }
    // erase
    while (~scanf("%d", &key)) {
        if (key == -1) break;
        printf("\n==== erase %d from red_black_tree ====\n", key);
        root = erase(root, key);
        output(root);
    }
    clear(root);
    return 0;
}

// 78 65 45 36 56 72 90 100 32 7 4 15 17 -1

B树

m 阶B树,定义与操作总结:

  1. 树中每个节点,最多含有m棵子树
  2. 根节点中关键字数量范围 1 ≤ n ≤ m − 1 1\leq n\leq m-1 1≤n≤m−1
  3. 非根结点中关键字数量范围 [ m / 2 ] 上取整 − 1 ≤ n ≤ m − 1 [m/2]_{上取整}-1\leq n\leq m-1 [m/2]上取整−1≤n≤m−1
  4. 插入调整为了解决 [上溢]节点,关键字数量为 m 的节点
  5. 删除调整为了解决 [下溢 ]节点,关键字数量为 [ m / 2 ] 上取整 − 2 [m/2]_{上取整}-2 [m/2]上取整−2 的节点插入
  6. 调整的核心操作是:节点分裂
  7. 删除调整的核心操作是:左旋右旋与合并

根节点只会发生关键字的上溢,不会发生下溢。

删除调整的时候:如果够借,就左旋或者右旋;不够借,就把父节点和兄弟节点和自己合并,构成一颗新的子树。

应用场景:

数据库中的数据大量的存储在磁盘上,而我们要是想计算的话,就需要把数据从硬盘调入到内存当中,涉及到磁盘的IO操作是需要很大的代价的,所以我们要尽量减少这种IO操作

B树当中的每个节点,就是磁盘当中的数据块,那么当B树在索引的时候,每次调入一整块节点,定位到下一块数据在磁盘中的位置,再去读一块数据进来,所以它可以减少磁盘和内存之间的数据交换次数

如果把内存当作高速设备,磁盘当作低速设备,那么B树就可以在低速设备和高速设备当中发挥作用。

速度设备:缓存->内存->磁盘->网络

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define MAX_M 3 // 关键字的数量上限
#define REPEAT_CNT 1    // 宏调试输出信息的技巧

#define LCHILD(root, pos) (root->next[pos])
#define RCHILD(root, pos) (root->next[pos + 1])
#define FIRST_CHILD(root) (root->next[0])
#define LAST_CHILD(root) (root->next[root->n])

#if REPEAT_CNT
int repeat_insert_cnt = 0;
#endif

typedef struct Node {
    int n;  // 关键字数量
    int key[MAX_M + 1]; // 关键字
    struct Node *next[MAX_M + 1]; // 最多MAX_M + 1棵子树
} Node;

Node *getNewNode() {
    Node *p = (Node *)malloc(sizeof(Node));
    p->n = 0;
    memset(p->next, 0, sizeof(Node *) * (MAX_M + 1));
    return p;
}
Node *insert_key(Node *root, int key) {
    if (root == NULL) {
        root = getNewNode();
        root->key[(root->n)++] = key;
        return root;
    }
    int pos = 0;
    while (pos < root->n && root->key[pos] < key) pos += 1;  
    if (pos < root->n && root->key[pos] == key) {
        // 如果重复插入了,直接退出
        #if REPEAT_CNT
        repeat_insert_cnt += 1;
        #endif
        return root;
    } 
    for (int i = root->n - 1; i >= pos; i--) {
        root->key[i + 1] = root->key[i];
    }
    root->key[pos] = key;
    root->n += 1;
    return root;
}

// 我这里说的分裂节点,指的是B树中的一个节点中包含的许多个关键字中的最中间的要被分裂的关键字所处的位置,是逻辑上的分裂节点
Node *insert_maintain(Node *root, Node *child, int pos) {
    if (child->n < MAX_M) return root;  // 关键字数量小于MAX_M说明没必要调整
    // 执行到这里说明关键字的数量==MAX_M,要进行插入调整
    int spos = MAX_M / 2; // spos为要分裂的节点喜下标
    Node *node1 = getNewNode();
    Node *node2 = getNewNode();
    node1->n = spos; // 左子树包含spos个关键字
    node2->n = MAX_M - 1 - spos; // 右子树包含去掉分列节点的关键字之后的剩下的关键字的数量
    for (int i = 0; i < node1->n; i++) { 
        // 复制左子树的信息
        node1->key[i] = child->key[i];
        node1->next[i] = child->next[i];
    }
    // 把分裂节点的左子树让渡给node1,作为node1的新的右子树
    node1->next[node1->n] = child->next[node1->n];
    for (int i = 0; i < node2->n; i++) {
        // 复制右子树的信息
        node2->key[i] = child->key[spos + 1 + i];
        node2->next[i] = child->next[spos + 1 + i];
    }
    // 把child中的最后一棵子树,让渡给node2,作为node2的最后一棵右子树
    node2->next[node2->n] = child->next[child->n];
    for (int i = root->n; i >= pos; i--) {     // 为了把next数组中的内容和key数组中的内容一起往后移动一位,i就从root->n开始了
        // 通过例子不难看出,如果子树的位置是pos,那么插入到父节点的位置也是pos,所以只需把父节点pos之后的元素往右移
        root->key[i + 1] = root->key[i];
        root->next[i + 1] = root->next[i];
    }
    root->key[pos] = child->key[spos];  // 插入关键字
    root->next[pos] = node1;        
    root->next[pos + 1] = node2;    // 连接左右子树
    root->n += 1;   // 父节点关键字数量+1
    free(child);    // 回收child
    return root;
}
Node *__insert(Node *root, int key) {
    if (root == NULL || root->next[0] == NULL) {
        // 如果是空树或者终端节点,直接插入关键字
        return insert_key(root, key);
    }
    // 执行到这里,说明为非终端节点
    int pos = 0;
    while (pos < root->n && root->key[pos] < key) pos += 1;     // 找到第一个大于要插入的关键字的位置
    if (pos < root->n && root->key[pos] == key) {
        // 如果重复插入了,直接退出
        #if REPEAT_CNT
        repeat_insert_cnt += 1;
        #endif
        return root;
    }    
    __insert(root->next[pos], key);   // key关键字应当插入在pos这个位置。
    return insert_maintain(root, root->next[pos], pos);
}
Node *insert(Node *root, int key) {
    // 包一层,解决处理根节点的问题,因为根节点没有父节点
    root = __insert(root, key);
    if (root->n == MAX_M) {
        Node *p = getNewNode();
        p->next[0] = root;
        root = insert_maintain(p, root, 0);
    }
    return root;
}
Node *erase_pos(Node *root, int pos) {
    for (int i = pos + 1; i < root->n; i++) {
        root->key[i - 1] = root->key[i];
    }
    root->n -= 1;
    return root;
}
void right_rotate(Node *root, int pos) {
    // pos关键字下降到右子树的第一个位置
    for (int i = RCHILD(root, pos)->n + 1; i > 0; i--) {
        // 右子树原有的关键字往后移动一位
        RCHILD(root, pos)->key[i] = RCHILD(root, pos + 1)->key[i - 1];
        RCHILD(root, pos)->next[i] = RCHILD(root, pos + 1)->next[i - 1];
    }
    RCHILD(root, pos + 1)->key[0] = root->key[pos]; // pos关键字下降到右子树的第一个位置
    FIRST_CHILD(RCHILD(root, pos + 1)) = LAST_CHILD(LCHILD(root, pos)); // 左子树的最后一个孩子给右子树
    LAST_CHILD(LCHILD(root, pos)) = NULL;
    RCHILD(root, pos)->n += 1;  // 右子树关键字数量+1
    // 左子树最后一个关键字上来
    root->key[pos] = LCHILD(root, pos)->key[LCHILD(root, pos)->n - 1];
    LCHILD(root, pos)->n -= 1;  // 左子树关键字数量-1
    return ;
}
void left_rotate(Node *root, int pos) {
    LCHILD(root, pos)->key[LCHILD(root, pos)->n] = root->key[pos];  // 根节点放到左子树去
    LAST_CHILD(LCHILD(root, pos)) = FIRST_CHILD(RCHILD(root, pos));
    root->key[pos] = RCHILD(root, pos)->key[0]; // 右子树的第一个关键字提到根节点上来
    for (int i = 0; i < RCHILD(root, pos)->n; i++) {
        // 把右子树的关键字往前移动一位
        RCHILD(root, pos)->key[i] = RCHILD(root, pos)->key[i + 1];
        RCHILD(root, pos)->next[i] = RCHILD(root, pos)->next[i + 1];
    }
    LAST_CHILD(RCHILD(root, pos)) = NULL;   // 把右子树最后一个孩子赋值为空
    LCHILD(root, pos)->n += 1;  // 左子树关键字数量+1
    RCHILD(root, pos)->n -= 1;  // 右子树关键字数量-1
    return ;
}
void merge_node(Node *root, int pos) {
    Node *node = getNewNode();
    // 把左子树放进来
    for (int i = 0; i <= LCHILD(root, pos)->n; i++) {
        node->key[i] = LCHILD(root, pos)->key[i];
        node->next[i] = LCHILD(root, pos)->next[i];
    }
    node->n = LCHILD(root, pos)->n + 1;
    node->key[node->n - 1] = root->key[pos];
    // 把右子树放进来
    for (int i = 0; i <= RCHILD(root, pos)->n; i++) {
        node->key[node->n + i] = RCHILD(root, pos)->key[i];
        node->next[node->n + i] = RCHILD(root, pos)->next[i];
    }
    node->n += RCHILD(root, pos)->n;
    free(LCHILD(root, pos));
    free(RCHILD(root, pos));
    for (int i = pos + 1; i <= root->n; i++) {
        root->key[i - 1] = root->key[i];
        root->next[i - 1] = root->next[i];
    }
    root->n -= 1;
    root->next[pos] = node;
    return ;
}
Node *erase_maintain(Node *root, int pos) {
    int lower_bound = (MAX_M + 1) / 2 - 1;
    if (root->next[pos]->n >= lower_bound) return root; // 如果子树的数量大于下界,则没必要调整
    // 代码执行到这里说明,当前子树发生了下溢,首先看看左兄弟能不能借一个,也就是右旋
    if (pos > 0 && root->next[pos - 1]->n > lower_bound) {
        right_rotate(root, pos - 1);
    } else if (pos < root->n && root->next[pos + 1]->n > lower_bound) {
        // 左旋
        left_rotate(root, pos);
    } else {
        // 合并
        if (pos > 0) merge_node(root, pos - 1);
        else merge_node(root, pos);
    }
    return root;
}
Node *__erase(Node *root, int x) {
    if (root == NULL) return root;
    int pos = 0;
    if (root->next[0] == NULL) {
        // 1. 在终端节点处删除
        while (pos < root->n && root->key[pos] < x) pos += 1;
        if (root->key[pos] == x) erase_pos(root, pos);
        return root;
    } else {
        while (pos < root->n && root->key[pos] < x) pos += 1;
        if (pos < root->n && root->key[pos] == x) {
            Node *temp = root->next[pos];   // temp找到当前要删除节点的前驱
            while (temp->next[temp->n]) temp = temp->next[temp->n];
            int val = temp->key[temp->n - 1];
            root->key[pos] = val;   // 交换
            root->next[pos] = __erase(root->next[pos], val); // 去左子树中删除前驱节点
        } else {
            root->next[pos] = __erase(root->next[pos], x);  // 当前节点没找到,就去pos子树中找
        }        
    }
    return erase_maintain(root, pos);   // 删除调整:检查pos位置的子树是否失衡
}
Node *erase(Node *root, int x) {
    root = __erase(root, x);
    if (root && root->n == 0) {
        Node *temp = root->next[0];
        free(root);
        root = temp;    // 根节点关键字为空,不一定代表这棵树就是空的,还有可能他下面挂着一个子树
    }
    return root;
}
void clear(Node *root) {
    if (root == NULL) return ;
    if (root->next[0] != NULL) {    // 非终端节点,循环回收子树
        for (int i = 0; i <= root->n; i++) {
            clear(root->next[i]);
        }
    }
    free(root);
    return ;
}
void print_node(Node *root) {
    printf("%d : ", root->n);
    for (int i = 0; i < root->n; i++) {
        printf("%4d", root->key[i]);
    }
    printf(" |");
    if (root->next[0] != NULL) {
        for (int i = 0; i <= root->n; i++) {
            printf("%4d", root->next[i]->key[0]);
        }
    }
    printf("\n");
    return ;
}
void output(Node *root) {
    if (root == NULL) return ;
    print_node(root);
    for (int i = 0; i <= root->n; i++) {
        output(root->next[i]);
    }
    return ;
}
int main() {
    srand(time(0));
    Node *root = NULL;
    #define MAX_OP 12 // 随即插入20个节点
    for (int i = 0; i < MAX_OP; i++) {
        int val = rand() % 500;
        root = insert(root, val);
        printf("\ninsert %d to Btree : \n", val);
        output(root);
    }
    #undef MAX_OP
    #if REPEAT_CNT
    printf("repeat insert %d iterm(s)\n", repeat_insert_cnt);
    #endif
    int x;
    while (scanf("%d", &x)) {
        root = erase(root, x);
        output(root);
    }
    return 0;
}

拓扑排序

  • 求所有拓扑排序序列:
c 复制代码
#include <iostream>
#include <vector>
using namespace std;
int n, m, ans[105], in_degree[105], mark[105]; // ans存储答案序列,in_degree存储每个点的入度

void func(int now, vector<vector<int>> &edg) {
    // now表示现在求的是序列中的第几个元素
    for (int i = 1; i <= n; i++) {  // 扫描每个点的入度是否为0
        if (now == n + 1) {
            // 序列填完了,直接输出
            for (int i = 1; i <= n; i++) {
                cout << ans[i] << " ";
            }
            cout << endl;
            return ;
        }
        if (in_degree[i] == 0 && mark[i] == 0) {
            ans[now] = i;
            mark[i] = 1;    // 将当前节点标记
            for (int j = 0; j < edg[i].size(); j++) {
                in_degree[edg[i][j]]--; // 去掉这条边之后,相应的入度-1
            }
            func(now + 1, edg);  // 递归
            for (int j = 0; j < edg[i].size(); j++) {  // 回溯
                in_degree[edg[i][j]]++;
            }
            mark[i] = 0;
        }
    }    
}
int main() {
    cin >> n >> m;
    vector<vector<int>> edg(n + 1, vector<int>());
    // 构建图
    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        edg[a].push_back(b); // a指向b的边
        in_degree[b]++; // b的入度++
    }
    func(1, edg);

    return 0;
}
// 输入:
7 8
1 2
3 2
3 4
3 5
2 5
4 6
5 7
6 7

// 输出:
1 3 2 4 5 6 7 
1 3 2 4 6 5 7 
1 3 2 5 4 6 7 
1 3 4 2 5 6 7 
1 3 4 2 6 5 7 
1 3 4 6 2 5 7 
3 1 2 4 5 6 7 
3 1 2 4 6 5 7 
3 1 2 5 4 6 7 
3 1 4 2 5 6 7 
3 1 4 2 6 5 7 
3 1 4 6 2 5 7 
3 4 1 2 5 6 7 
3 4 1 2 6 5 7 
3 4 1 6 2 5 7 
3 4 6 1 2 5 7 

排序(插入、冒泡、归并、选择、快排、快排优化)

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

#define swap(a, b) { \
    __typeof(a) __tmp = a; \
    a = b; b = __tmp; \
}

#define TEST(arr, n, func, args...) { \
    int *brr = (int *)malloc(sizeof(int) * n); \
    memcpy(brr, arr, sizeof(int) * n); \
    func(args); \
    printf("%s = ", #func); \
    showArr(brr, n); \
    free(brr); \
}

void insert_sort(int *arr, int len) {
    int i, j;
    for (i = 1; i < len; i++) {
        for (j = i; j > 0 && arr[j] < arr[j - 1]; j--) {
            swap(arr[j], arr[j - 1]);
        }
    }
    return ;
}
void bubble_sort(int *arr, int len) {
    int i, j;
    for (i = 1; i < len; i++) {
        for (j = 0; j < len - i; j++) {
            if (arr[j] > arr[j + 1]) swap(arr[j], arr[j + 1]);
        }
    }
    return ;
}
void merge_sort(int *arr, int l, int r) {
    if (l == r) return ;
    int mid = (l + r) >> 1;
    merge_sort(arr, l, mid);
    merge_sort(arr, mid + 1, r);
    int *tmp = (int *)malloc(sizeof(int) * (r - l + 1));
    int p1 = l, p2 = mid + 1, k = 0;
    while (p1 <= mid || p2 <= r) {
        if (p2 > r || (p1 <= mid && arr[p1] < arr[p2])) {
            tmp[k++] = arr[p1++];
        } else {
            tmp[k++] = arr[p2++];
        }
    }
    memcpy(arr + l, tmp, sizeof(int) * (r - l + 1));
    free(tmp);
    return ;
}
void select_sort(int *arr, int len) {
    int i, j, minidx;
    for (i = 0; i < len - 1; i++) {
        minidx = i;
        for (j = i + 1; j < len; j++) {
            if (arr[minidx] > arr[j]) {
                minidx = j;
            }
        }
        swap(arr[i], arr[minidx]);
    }
    return ;
}
void quick_sort(int *arr, int l, int r) {
    if (l > r) return ;
    int x = l, y = r, z = arr[x];
    while (x < y) {
        while (x < y && arr[y] > z) y--;
        if (x < y) arr[x++] = arr[y];
        while (x < y && arr[x] < z) x++;
        if (x < y) arr[y--] = arr[x];
    }
    arr[x] = z;
    quick_sort(arr, l, x - 1);
    quick_sort(arr, x + 1, r);
    return ;
}
void quick_sort_opt(int *arr, int l, int r) {
    while (l < r) {
        int x = l, y = r, z = arr[(l + r) >> 1];
        do {
            while (arr[y] > z) y--;
            while (arr[x] < z) x++;
            if (x <= y) {
                swap(arr[x], arr[y]);
                x++;
                y--;
            }
        } while (x <= y);
        quick_sort_opt(arr, l, y);
        l = x;  // 单边递归优化
    }
    return ;
}
void showArr(int *arr, int n) {
    printf("[");
    int i;
    for (i = 0; i < n; i++) {
        i && printf(",");
        printf("%d", arr[i]);
    }
    printf("]\n");
    return ;
}
int main() {
    srand(time(0));
    #define ARRLEN 10
    int arr[ARRLEN];
    int i;
    for (i = 0; i < ARRLEN; i++) {
        arr[i] = rand() % 100;
    }
    showArr(arr, ARRLEN);
    TEST(arr, ARRLEN, insert_sort, brr, ARRLEN);
    TEST(arr, ARRLEN, bubble_sort, brr, ARRLEN);
    TEST(arr, ARRLEN, select_sort, brr, ARRLEN);
    TEST(arr, ARRLEN, merge_sort, brr, 0, ARRLEN -1 );
    TEST(arr, ARRLEN, quick_sort, brr, 0, ARRLEN -1 );
    TEST(arr, ARRLEN, quick_sort_opt, brr, 0, ARRLEN -1 );

    return 0;
}

希尔排序

c 复制代码
// 课本上实现方式, 不带哨兵
void InsertSort(int A[], int n) {
    int i, j, temp;
    for (i = 1; i < n; i++) {
        if (A[i] < A[i - 1]) {
            temp = A[i];
            for (j = i - 1; j >= 0 && A[j] > temp; --j) {
                A[j + 1] = A[j];
            }
            A[j + 1] = temp;
        }
    }
}
// 课本上实现方式, 带哨兵
void InsertSortOpt(int A[], int n) {
    int i, j;
    for (i = 2; i <= n; i++) {   // 依次将A[2]~A[n] 插入到前面的有序序列
        if (A[i] < A[i - 1]) {
            A[0] = A[i];         // 复制为哨兵,A[0] 不存放元素,将A[i] 插入有序表
            for (j = i - 1; A[j] > A[0]; --j) { // 从后往前查找待插入位置
                A[j + 1] = A[j]; // 向后挪位
            }
            A[j + 1] = A[0];     // 复制到插入位置
        }
    }
}
// 优化------折半插入排序
void InsertSortBinary(int A[], int n) {
    int i, j, high, low, mid;
    for (i = 2; i < n; i++) {
        A[0] = A[i];
        low = 1; high = i - 1;
        while (low <= high) {
            mid = (high + low) >> 1;
            if (A[mid] > A[0]) high = mid - 1;
            else low = mid + 1;   // 查找右半子表
        }
        for (j = i - 1; j >= low; --j) {
            A[j + 1] = A[j];      // 统一后移操作
        }
        A[low] = A[0];            // 插入操作
    }
}
// 希尔排序
void ShellSort(int A[], int n) {
    int d, i, j;
    for (d = n / 2; d >= 1; d = d / 2) {
        for (i = d + 1; i <= n; ++i) {
            if (A[i] < A[i - d]) {
                A[0] = A[i];
                for (j = i - d; j > 0 && A[0] < A[j]; j -= d) {
                    A[j + d] = A[j];
                }
                A[j + d] = A[0];
            }
        }
    }
}

dijsktra算法-单源最短路

c 复制代码
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

struct edge {
    int e, v, next;
};
struct node {
    int now, dis;
    bool operator< (const node &b) const {
        return this->dis > b.dis;
    }
};
edge edg[200005];
int n, m, s, ans[100005], head[100005], cnt;

void add_edge(int a, int b, int c) {
        edg[cnt].e = b;
        edg[cnt].v = c;
        edg[cnt].next = head[a];
        head[a] = cnt;
        cnt++;
}   
int main() {
    memset(head, -1, sizeof(head));
    memset(ans, 0x3f, sizeof(ans));
    scanf("%d%d%d", &n, &m, &s);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        add_edge(a, b, c);
        add_edge(b, a, c);
    }
    priority_queue<node> que;
    que.push((node){s, 0});
    ans[s] = 0;
    while (!que.empty()) {
        node temp = que.top();
        que.pop();
        if (temp.dis > ans[temp.now]) {
            continue;
        }
        for (int i = head[temp.now]; i!= -1; i = edg[i].next) {
            int e = edg[i].e, v = edg[i].v;
            if (ans[e] > ans[temp.now] + v) {
                ans[e] = ans[temp.now] + v;
                que.push((node){e, ans[e]});
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        if (ans[i] == 0x3f3f3f3f) {
            cout << -1 << endl;
        } else {
            cout << ans[i] << endl;
        }
    }
    return 0;
}

kruskal算法-最小生成树

c 复制代码
#include <iostream>
#include <algorithm>
using namespace std;
struct edge {
    int s, e, v;
    bool operator< (const edge &b) const {
        return this->v < b.v;
    }
};
edge edg[200005];
int n, m, ans, my_union[5005], cnt;

void init() {
    for (int i = 1; i <= n; i++) {
        my_union[i] = i;
    }
    return ;
}
int find_fa(int x) {
    if (my_union[x] == x) {
        return x;
    }
    return my_union[x] = find_fa(my_union[x]);
}
int main() {
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        cin >> edg[i].s >> edg[i].e >>  edg[i].v;
    }
    sort(edg, edg + m);
    init();
    for (int i = 0; i < m; i++) {
        int s = edg[i].s, e = edg[i].e, v = edg[i].v;
        int fa = find_fa(s), fb = find_fa(e);
        if (fa != fb) {
            my_union[fa] = fb;
            ans += v;
            cnt++;
            if (cnt == n - 1) {
                cout << ans << endl;
                return 0;
            }
        }
    }
    cout << "-1" << endl;
    return 0;
}

Prim算法-最小生成树

c 复制代码
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
// 不断拿到权值最小的边,然后不断向外延伸。
struct node {   // 绿色的边:记录链接到哪,权值多少
    int e, v;
    bool operator<(const node &b) const {
        return this->v > b.v;  // 从小到大,逻辑反着写
    }
};
// 链式前向星的边集
struct edge {   
    int e, v, next;
};
edge edg[400005];
int n, m, head[5005], mark[5005], ans, cnt, edg_cnt, num[5005]; // num[x] 表示连接到x的边的权值
void add_edg(int a, int b, int c) {
    edg[edg_cnt].e = b;
    edg[edg_cnt].v = c;
    edg[edg_cnt].next = head[a];
    head[a] = edg_cnt++;
}
int main() {
    memset(head, -1, sizeof(head));
    memset(num, 0x3f, sizeof(num));
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        add_edg(a, b, c);     
        add_edg(b, a, c);     
    }
    priority_queue<node> que;
    que.push((node){n / 2, 0});
    while (!que.empty()) {
        node temp = que.top();
        que.pop();
        if (mark[temp.e] == 1) {
            continue;
        }
        ans += temp.v;
        mark[temp.e] = 1;
        cnt++;
        if (cnt == n) {
            cout << ans << endl;
            return 0;
        }
        for (int i = head[temp.e]; i != -1; i = edg[i].next) {
            int e = edg[i].e, v = edg[i].v;
            if (mark[e] == 0 && num[e] > v) {
                que.push((node){e, v});
                num[e] = v;
            }
        }
    }
    cout << "-1" << endl;
    return 0;
}

字符串匹配算法汇总(朴素、哈希匹配、kmp、kmp优化、Sunday、Shift-And)

  • 朴素匹配算法:

    时间复杂度O(n*m),可以作为匹配算法的基线水平。你总不能比暴力朴素匹配算法性能还差吧。

  • 哈希匹配:

    时间复杂度约等于O(n),因为哈希值对P取余 P若约等于m

    减少哈希冲突:p一般设置成一个质数,把余数出现的种类尽可能多:p-1,这样冲突概率小。

    base也尽量设置成一个质数。

    合理地选择p是提速的关键:尽量设置成大素数。

  • kmp

    简单来说kmp算法跟暴力匹配的区别就是,他每次拿母串的每一位对模式串进行匹配的时候,匹配的是母串当前位的前一位所能匹配到模式串最长前缀的下一位。如下图所示,a的前一位是e,e所能匹配到模式串的最长前缀是下标为1的位置,它的下一位是c,所以a最终要跟c进行匹配。

  • kmp优化

    把next最终要指向的位置每一种情况在运算的过程中一并给算好。在迁移的过程中,拷贝一份之前的,并更新需要改动的选项。根据next数组获取每个位置要跳转的信息。其中,-1位的跳转信息是0

    底层思想是NFA->DFA:
    NFA:不确定性有穷状态自动机 。(j到底跳转到哪需要用一个while循环去尝试的,给人一种不确定的感觉。)
    DFA:确定性有穷状态自动机。 (你给我一个数,我下一步跳转到哪很确定,直接抵达了。)

    就运行效率而言一定是DFA比NFA更高效。

  • Sunday算法:
    黄金对齐点位: 找到模式串向后推一位,对应文本串最后的那个位置的元素。然后再去从后向前这个找模式串中这个元素出现位置。将他们俩对齐开始匹配。

    所以需要维护各个元素最后出现的位置。当出现模式串中没有的字符的时候,就直接让模式串的-1位和当前字符对齐。也就是直接让文本串中的指针向后推移该元素最后出现的位置在模式串中的次序

  • Shift-And 算法:

    把模式串预处理为各种字符的编码信息:

    如果相应二进制位上是1,则代表该字符在模式串上出现过。

    编码完以后就拿着这张编码去匹配了。
    P = (P << 1 | 1) & d[s[i]]
    P & (1 << (n - 1)) ?= 1

    其实底层原理和kmp是一样的。(NFA)

    只不过用二进制表示出来。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_N 10000
#define DEFAULT_LEN 50
char s[MAX_N + 5], t[MAX_N + 5];

#define TEST(func) { \
    char temp_s[MAX_N + 5] = {0}; \
    sprintf(temp_s, "%s(\"%s\", \"%s\") = %3d\n", #func, s, t, func(s, t)); \
    int n = DEFAULT_LEN - strlen(temp_s); \
    while (n >= 0) n -= printf(" "); \
    printf("%s", temp_s); \
}

int brute_one_match(const char *s, const char *t) {
    printf("brute_one_match called\n");
    for (int j = 0; t[j]; j++) {
        if (s[j] == t[j]) continue;
        return 0;
    }
    return 1;
}

int brute_force(const char *s, const char *t) {
    for (int i = 0; s[i]; i++) {
        if (brute_one_match(s + i, t)) return i;
    }
    return -1;
}

int quick_mod(int a, int b, int c) {
    int ans = 1, temp = a;
    while (b) {
        if (b & 1) ans = ans * temp % c;
        temp = temp * temp % c;
        b >>= 1;
    }
    return ans;
}

int hash_match(const char *s, const char *t) {
    int len = strlen(t), base = 31, P = 9973, nbase = quick_mod(base, len, P);
    int h = 0, th = 0;
    for (int i = 0; t[i]; i++) th = (th * base + t[i]) % P;
    for (int i = 0; s[i]; i++) {
        h = (h * base + s[i]) % P;
        if (i >= len) h = (h - (s[i - len] * nbase % P) + P) % P;
        if (i + 1 < len) continue;
        if (h != th) continue;
        if (brute_one_match(s + i - len + 1, t)) return i - len + 1;
    }
    return -1;
}

int *getNext(const char *t, int *n) {
    *n = strlen(t);
    int *next = (int *)malloc(sizeof(int) * (*n));
    next[0] = -1;
    for (int i = 1, j = -1; t[i]; i++) {
        while (j != -1 && t[j + 1] != t[i]) j = next[j];
        if (t[j + 1] == t[i]) j += 1;
        next[i] = j;
    }
    return next;
}

void free_next(int *next) {
    free(next);
    return ;
}

void free_jump(int **jump, int n) {
    for (int i = 0; i < n; i++) free(jump[i - 1]);
    free(jump - 1);
    return ;
}

int **getJump(int *next,  const char *t, int n) {
    int **jump = (int **)malloc(sizeof(int *) * n);
    for (int i = 0; i < n; i++) jump[i] = (int *)malloc(sizeof(int) * 26);
    jump += 1;
    for (int i = 0; i < 26; i++) jump[-1][i] = -1; 
    jump[-1][t[0] - 'a'] = 0;
    for (int i = 0, I = n - 1; i < I; i++) {
        for (int j = 0; j < 26; j++) jump[i][j] = jump[next[i]][j];
        jump[i][t[i + 1] - 'a'] = i + 1;
    }
    return jump;
}

int kmp(const char *s, const char *t) {
    int len;
    int *next = getNext(t, &len);
    for (int i = 0, j = -1; s[i]; i++) {
        while (j != -1 && t[j + 1] != s[i]) j = next[j];
        if (t[j + 1] == s[i]) j += 1;
        if (t[j + 1] == '\0') {
            free_next(next);
            return i - len + 1;
        }
    }
    free_next(next);
    return -1;
}


int kmp_opt(const char *s, const char *t) {
    int len;
    int *next = getNext(t, &len);
    int **jump = getJump(next, t, len);
    for (int i = 0, j = -1; s[i]; i++) {
        j = jump[j][s[i] - 'a'];
        if (j == len - 1) {
            free_next(next);
            free_jump(jump, len);
            return i - len + 1;
        }
    }
    free_next(next);
    free_jump(jump, len);
    return -1;
}
int sunday(const char *s, const char *t) {
    int tlen=strlen(t),slen=strlen(s);
    int jump[128]={0};//每一种字符出现在第几位。
    for(int i=0;i<128;i++) jump[i]=tlen+1;//默认是tlen+1;
    for(int i=0;t[i];i++) jump[t[i]]=tlen-i;//维护每个字符最后出现的位置。
    for(int i=0;i+tlen<=slen;){
        if(brute_one_match(s+i,t)) return i;
        i+=jump[s[i+tlen]];
    }
    return -1;
}

int shift_and(const char *s, const char *t) {
    int code[128] = {0}, n = 0;
    for (;t[n];n++) code[t[n]] |= (1<<n);
    int p = 0;
    for (int i=0;s[i];i++) {
        p=(p<<1 | 1) & code[s[i]];
        if (p & (1 << (n-1))) return i-n+1;
    }
    return -1;
}

int test_func(const char *s, const char *t) {
    return -1;
}

int main() {
    while (~scanf("%s%s", s, t)) {
        TEST(brute_force);
        TEST(hash_match);
        TEST(kmp);
        TEST(kmp_opt);
        TEST(sunday);
        TEST(shift_and);
    }
    return 0;
}
相关推荐
ahadee23 分钟前
蓝桥杯每日真题 - 第7天
c++·vscode·算法·蓝桥杯
AnFany23 分钟前
LeetCode【0051】N皇后
python·算法·leetcode·回溯法·n皇后
可别是个可爱鬼23 分钟前
代码随想录 -- 动态规划 -- 完全平方数
数据结构·python·算法·leetcode·动态规划
青椒大仙KI1126 分钟前
24/11/14 算法笔记 EM算法期望最大化算法
人工智能·笔记·算法
三小尛26 分钟前
选择排序(C语言)
数据结构
一直学习永不止步28 分钟前
LeetCode题练习与总结:至少有 K 个重复字符的最长子串--395
java·算法·leetcode·字符串·滑动窗口·哈希表·分治
XY.散人29 分钟前
初识算法 · 位运算(2)
算法
迷迭所归处31 分钟前
动态规划 —— 子数组系列-环形子数组的最大和
算法·动态规划
wx200411021 小时前
树形dp总结
算法·深度优先·图论
妈妈说名字太长显傻1 小时前
【数据结构】交换排序——冒泡排序 和 快速排序
数据结构·算法·排序算法