串(String) string类
用到了string.h的一些操作,应该学学,上网搜一下就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char *str; int length; } MyString; MyString initString (const char *source) { MyString s; s.length = strlen (source); s.str = (char *)malloc ((s.length + 1 ) * sizeof (char )); strcpy (s.str, source); return s; } void destroyString (MyString *s) { if (s->str) { free (s->str); s->str = NULL ; s->length = 0 ; } } void assignString (MyString *s, const char *source) { if (s->str) free (s->str); s->length = strlen (source); s->str = (char *)malloc ((s->length + 1 ) * sizeof (char )); strcpy (s->str, source); } int compareString (MyString s1, MyString s2) { return strcmp (s1.str, s2.str); } int getStringLength (MyString s) { return s.length; } MyString concatString (MyString s1, MyString s2) { MyString result; result.length = s1.length + s2.length; result.str = (char *)malloc ((result.length + 1 ) * sizeof (char )); strcpy (result.str, s1.str); strcat (result.str, s2.str); return result; } MyString substring (MyString s, int pos, int len) { MyString sub; if (pos < 0 || pos >= s.length || len <= 0 || pos + len > s.length) { sub.str = NULL ; sub.length = 0 ; return sub; } sub.length = len; sub.str = (char *)malloc ((len + 1 ) * sizeof (char )); strncpy (sub.str, s.str + pos, len); sub.str[len] = '\0' ; return sub; } int isEmpty (MyString s) { return s.length == 0 ; } void clearString (MyString *s) { if (s->str) { free (s->str); s->str = NULL ; } s->length = 0 ; } int findSubstring (MyString s, MyString sub) { char *pos = strstr (s.str, sub.str); return pos ? (int )(pos - s.str) : -1 ; } MyString replaceSubstring (MyString s, MyString oldSub, MyString newSub) { MyString result = initString("" ); int pos; while ((pos = findSubstring(s, oldSub)) != -1 ) { MyString left = substring(s, 0 , pos); MyString right = substring(s, pos + oldSub.length, s.length - pos - oldSub.length); MyString temp = concatString(left, newSub); destroyString(&left); destroyString(&s); s = concatString(temp, right); destroyString(&temp); destroyString(&right); } result = s; return result; } MyString insertSubstring (MyString s, int pos, MyString sub) { if (pos < 0 || pos > s.length) return s; MyString left = substring(s, 0 , pos); MyString right = substring(s, pos, s.length - pos); MyString temp = concatString(left, sub); MyString result = concatString(temp, right); destroyString(&left); destroyString(&right); destroyString(&temp); return result; } MyString deleteSubstring (MyString s, int pos, int len) { if (pos < 0 || pos >= s.length || len <= 0 || pos + len > s.length) return s; MyString left = substring(s, 0 , pos); MyString right = substring(s, pos + len, s.length - pos - len); MyString result = concatString(left, right); destroyString(&left); destroyString(&right); return result; } int main () { MyString s1 = initString("Hello" ); MyString s2 = initString("World" ); MyString s3 = concatString(s1, s2); printf ("Concat: %s\n" , s3.str); MyString sub = substring(s3, 5 , 5 ); printf ("Substring: %s\n" , sub.str); int pos = findSubstring(s3, initString("World" )); printf ("Find position: %d\n" , pos); MyString replaced = replaceSubstring(s3, initString("World" ), initString("Everyone" )); printf ("Replaced: %s\n" , replaced.str); destroyString(&s1); destroyString(&s2); destroyString(&s3); destroyString(&sub); destroyString(&replaced); return 0 ; }
KMP算法
构建部分匹配表
部分匹配值 是模式串中每个位置的前缀与后缀的最大匹配长度。
构建 next 数组,用于指示在模式串失配后,主串的比较位置可以跳转的地方,从而减少重复比较。
匹配主串与模式串
遍历主串时,如果字符匹配,则继续比较。
如果字符失配,则根据 next 数组跳转模式串的匹配起点,而不是回退主串指针。 kmp还是很难的,也很难去描述,建议【最浅显易懂的 KMP 算法讲解-哔哩哔哩】 https://b23.tv/To4ex7N 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <stdio.h> #include <string.h> #include <stdlib.h> void buildNext (const char *pattern, int *next, int m) { int j = 0 ; next[0 ] = 0 ; for (int i = 1 ; i < m; i++) { while (j > 0 && pattern[i] != pattern[j]) { j = next[j - 1 ]; } if (pattern[i] == pattern[j]) { j++; } next[i] = j; } } int kmpSearch (const char *text, const char *pattern) { int n = strlen (text); int m = strlen (pattern); int *next = (int *)malloc (m * sizeof (int )); buildNext(pattern, next, m); int j = 0 ; for (int i = 0 ; i < n; i++) { while (j > 0 && text[i] != pattern[j]) { j = next[j - 1 ]; } if (text[i] == pattern[j]) { j++; } if (j == m) { free (next); return i - m + 1 ; } } free (next); return -1 ; } int main () { const char *text = "ababcabcacbab" ; const char *pattern = "abcac" ; int position = kmpSearch(text, pattern); if (position != -1 ) { printf ("Pattern found at index %d\n" , position); } else { printf ("Pattern not found.\n" ); } return 0 ; }
KMP算法的时间复杂度O(n + m)
数组和广义表 考概念居多,没代码实现,建议去看PPT
树和二叉树 树
树的定义 树是由 n (n≥ 0) 个结点组成的有限集合。 如果n= 0,称为空树; 否则,在一棵非空树中(n>0) ,满足如下两个条件: (1)有且仅有一个特定的称之为**根(root)的结点,它只有直接后继,但没有直接前驱; (2)除根以外的其它结点划分为 m (m ≥ 0) 个互不相 交的有限集合 T0,T1, …, Tm-1,每个集合又是一棵树,并且称之为根的 子树(subTree)**。
根结点 : 非空树中无前驱结点的节点
结点的度(degree) : 结点拥有的子树数
/叶子/终端结点: 度=0分支结点/非终端结点: 度≠0
部结点: 根结点以外的分支结点结点的子树的根称为该节点的孩子(Child),该结点称为孩子的 双亲(Parent)
同一双亲的孩子之间互称兄弟(Sibling)
树的深度(高度) : 树中结点的最大层次
建议学好递归再来学树
二叉树 哈夫曼编码 哈夫曼编码的一个重要性质是无前缀性质(Prefix Property),即任何字符的编码都不会是其他字符编码的前缀。 具体来说,哈夫曼树的路径保证了任何一个字符的编码不会是另一个字符编码的前缀。这是通过树的结构保证的:每个字符的编码对应哈夫曼树中从根到叶子的路径。由于我们构建树时,频率较高的字符更靠近根节点,因此它们的编码长度较短,而频率较低的字符的编码则较长。 只要是叶子就不会是其他编码的前缀或者是后缀 数据压缩编码 其实就是实现没有公共前缀的01二进制编码来优化存储空间 A 5 B 9 C 12 D 13 E 16 F 45 构建过程: 将权值最小的两个节点 A(5) 和 B(9) 合并为新节点 T1(14)。 将 T1(14) 和 C(12) 合并为新节点 T2(26)。 将 T2(26) 和 D(13) 合并为新节点 T3(39)。 将 T3(39) 和 E(16) 合并为新节点 T4(55)。 将 T4(55) 和 F(45) 合并为根节点 T5(100)。
1 2 3 4 5 6 7 8 9 10 [100 ] / \ [45 ] [55 ] / \ [26 ] [16 ] / \ [14 ] [12 ] / \ [5 ] [9 ]
很喜欢用堆去解释这些东西,堆可以理解成每次在一个集合中取最大或者最小的元素,也可以放入元素进入集合 利用堆去实现的效率会高很多,很轻松的去实现,先放这里,看不懂算了,学会理论就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 #include <stdio.h> #include <stdlib.h> typedef struct HuffmanNode { char data; int weight; struct HuffmanNode *left , *right ; } HuffmanNode; typedef struct MinHeap { int size; int capacity; HuffmanNode **array ; } MinHeap; HuffmanNode *createNode (char data, int weight) { HuffmanNode *node = (HuffmanNode *)malloc (sizeof (HuffmanNode)); node->data = data; node->weight = weight; node->left = node->right = NULL ; return node; } MinHeap *createMinHeap (int capacity) { MinHeap *heap = (MinHeap *)malloc (sizeof (MinHeap)); heap->size = 0 ; heap->capacity = capacity; heap->array = (HuffmanNode **)malloc (capacity * sizeof (HuffmanNode *)); return heap; } void swapNodes (HuffmanNode **a, HuffmanNode **b) { HuffmanNode *temp = *a; *a = *b; *b = temp; } void minHeapify (MinHeap *heap, int idx) { int smallest = idx; int left = 2 * idx + 1 ; int right = 2 * idx + 2 ; if (left < heap->size && heap->array [left]->weight < heap->array [smallest]->weight) smallest = left; if (right < heap->size && heap->array [right]->weight < heap->array [smallest]->weight) smallest = right; if (smallest != idx) { swapNodes(&heap->array [smallest], &heap->array [idx]); minHeapify(heap, smallest); } } HuffmanNode *extractMin (MinHeap *heap) { HuffmanNode *temp = heap->array [0 ]; heap->array [0 ] = heap->array [heap->size - 1 ]; heap->size--; minHeapify(heap, 0 ); return temp; } void insertMinHeap (MinHeap *heap, HuffmanNode *node) { heap->size++; int i = heap->size - 1 ; while (i > 0 && node->weight < heap->array [(i - 1 ) / 2 ]->weight) { heap->array [i] = heap->array [(i - 1 ) / 2 ]; i = (i - 1 ) / 2 ; } heap->array [i] = node; } MinHeap *buildMinHeap (char data[], int weight[], int size) { MinHeap *heap = createMinHeap(size); for (int i = 0 ; i < size; i++) heap->array [i] = createNode(data[i], weight[i]); heap->size = size; for (int i = (size - 2 ) / 2 ; i >= 0 ; i--) minHeapify(heap, i); return heap; } HuffmanNode *buildHuffmanTree (char data[], int weight[], int size) { MinHeap *heap = buildMinHeap(data, weight, size); while (heap->size > 1 ) { HuffmanNode *left = extractMin(heap); HuffmanNode *right = extractMin(heap); HuffmanNode *top = createNode('$' , left->weight + right->weight); top->left = left; top->right = right; insertMinHeap(heap, top); } return extractMin(heap); } void printCodes (HuffmanNode *root, int *code, int top) { if (root->left) { code[top] = 0 ; printCodes(root->left, code, top + 1 ); } if (root->right) { code[top] = 1 ; printCodes(root->right, code, top + 1 ); } if (!root->left && !root->right) { printf ("%c: " , root->data); for (int i = 0 ; i < top; i++) printf ("%d" , code[i]); printf ("\n" ); } } int main () { char data[] = {'A' , 'B' , 'C' , 'D' , 'E' , 'F' }; int weight[] = {5 , 9 , 12 , 13 , 16 , 45 }; int size = sizeof (data) / sizeof (data[0 ]); HuffmanNode *root = buildHuffmanTree(data, weight, size); int code[100 ], top = 0 ; printCodes(root, code, top); return 0 ; }
F: 0 C: 100 A: 1010 B: 1011 D: 110 E: 111 再贴上一个书上用遍历去实现的哈夫曼树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TREE_SIZE 100 typedef struct HuffmanNode { char data; int frequency; struct HuffmanNode * left ; struct HuffmanNode * right ; } HuffmanNode; HuffmanNode* createHuffmanNode (char data, int frequency) { HuffmanNode* node = (HuffmanNode*)malloc (sizeof (HuffmanNode)); node->data = data; node->frequency = frequency; node->left = node->right = NULL ; return node; } int compareNodes (const void * a, const void * b) { return ((HuffmanNode*)a)->frequency - ((HuffmanNode*)b)->frequency; } HuffmanNode* buildHuffmanTree (char * data, int * freq, int n) { HuffmanNode* nodes[MAX_TREE_SIZE]; for (int i = 0 ; i < n; i++) { nodes[i] = createHuffmanNode(data[i], freq[i]); } while (n > 1 ) { qsort(nodes, n, sizeof (HuffmanNode*), compareNodes); HuffmanNode* left = nodes[0 ]; HuffmanNode* right = nodes[1 ]; HuffmanNode* newNode = createHuffmanNode('\0' , left->frequency + right->frequency); newNode->left = left; newNode->right = right; nodes[0 ] = newNode; for (int i = 1 ; i < n - 1 ; i++) { nodes[i] = nodes[i + 1 ]; } n--; } return nodes[0 ]; } void printHuffmanTree (HuffmanNode* root, char * code, int length) { if (root == NULL ) return ; if (root->left == NULL && root->right == NULL ) { code[length] = '\0' ; printf ("%c: %s\n" , root->data, code); return ; } code[length] = '0' ; printHuffmanTree(root->left, code, length + 1 ); code[length] = '1' ; printHuffmanTree(root->right, code, length + 1 ); } int main () { char data[] = {'A' , 'B' , 'C' , 'D' , 'E' , 'F' }; int freq[] = {5 , 9 , 12 , 13 , 16 , 45 }; int n = sizeof (data) / sizeof (data[0 ]); HuffmanNode* root = buildHuffmanTree(data, freq, n); char code[MAX_TREE_SIZE]; printf ("Huffman Codes:\n" ); printHuffmanTree(root, code, 0 ); return 0 ; }
满二叉树 和 完全二叉树
满二叉树 一个二叉树如果所有层上的结点数都达到了最大值,则称为满二叉树。
1 2 3 4 5 6 1 / \ 2 3 / \ / \ 4 5 6 7
完全二叉树 一个二叉树如果其所有层(除了最后一层)都完全填满,并且最后一层的结点都靠左排列,则称为完全二叉树。
1 2 3 4 5 1 / \ 2 3 / \ / 4 5 6
树的建立,遍历 我认为看代码更能理解怎么前中后遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 #include <stdio.h> #include <stdlib.h> typedef struct TreeNode { int data; struct TreeNode *left ; struct TreeNode *right ; } TreeNode; TreeNode *createNode (int value) { TreeNode *newNode = (TreeNode *)malloc (sizeof (TreeNode)); newNode->data = value; newNode->left = NULL ; newNode->right = NULL ; return newNode; } TreeNode *buildTree () { int value; printf ("请输入节点值(-1表示为空):" ); scanf ("%d" , &value); if (value == -1 ) { return NULL ; } TreeNode *root = createNode(value); printf ("输入 %d 的左子节点:\n" , value); root->left = buildTree(); printf ("输入 %d 的右子节点:\n" , value); root->right = buildTree(); return root; } void preOrder (TreeNode *root) { if (root == NULL ) { return ; } printf ("%d " , root->data); preOrder(root->left); preOrder(root->right); } void inOrder (TreeNode *root) { if (root == NULL ) { return ; } inOrder(root->left); printf ("%d " , root->data); inOrder(root->right); } void postOrder (TreeNode *root) { if (root == NULL ) { return ; } postOrder(root->left); postOrder(root->right); printf ("%d " , root->data); } void freeTree (TreeNode *root) { if (root == NULL ) { return ; } freeTree(root->left); freeTree(root->right); free (root); } int main () { printf ("开始构建二叉树...\n" ); TreeNode *root = buildTree(); printf ("\n前序遍历结果:" ); preOrder(root); printf ("\n中序遍历结果:" ); inOrder(root); printf ("\n后序遍历结果:" ); postOrder(root); freeTree(root); return 0 ; }
前序遍历结果:1 2 3 4 中序遍历结果:2 3 1 4 后序遍历结果:3 2 4 1
要想确定树的样子一定要有中序遍历,然后去根据定义一递归的去找左右就好了 多练习两个看看就好了 示例 假设: 前序遍历:A B D E C F G 中序遍历:D B E A F C G 构建过程: 从前序遍历中,A 是根节点。 在中序遍历中,A 将序列分为两部分: 左子树的中序序列:D B E 右子树的中序序列:F C G 从前序遍历的剩余部分: 左子树的前序序列:B D E 右子树的前序序列:C F G 递归构建左右子树: 左子树的根节点是 B。 右子树的根节点是 C。
1 2 3 4 5 6 7 A / \ B C / \ \ D E F \ G
中序遍历是分割子树的关键,前序或后序遍历提供了根节点的顺序。
线索二叉树 是一种对普通二叉树的扩展。通过利用二叉树中指针的空闲空间,将树中节点的左右指针扩展为指向前驱节点和后继节点,使遍历树更加高效。 就是可以不每一次从头节点开始遍历 前序线索二叉树:基于前序遍历建立的线索二叉树。 中序线索二叉树:基于中序遍历建立的线索二叉树。 后序线索二叉树:基于后序遍历建立的线索二叉树。特点 线索二叉树在节点结构中新增了两个布尔变量,用来标记左右指针是否是线索: LTag:标记左指针的含义,0 表示指向左子树,1 表示指向前驱。 RTag:标记右指针的含义,0 表示指向右子树,1 表示指向后继。 线索二叉树通过线索化处理后,可以通过线索指针快速找到前驱和后继节点,而无需递归或栈辅助。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <stdio.h> #include <stdlib.h> typedef struct ThreadedTreeNode { int data; struct ThreadedTreeNode *left ; struct ThreadedTreeNode *right ; int LTag; int RTag; } ThreadedTreeNode; ThreadedTreeNode *pre = NULL ; ThreadedTreeNode *createNode (int data) { ThreadedTreeNode *node = (ThreadedTreeNode *)malloc (sizeof (ThreadedTreeNode)); node->data = data; node->left = node->right = NULL ; node->LTag = 0 ; node->RTag = 0 ; return node; } void inThreading (ThreadedTreeNode *root) { if (root == NULL ) return ; inThreading(root->left); if (root->left == NULL ) { root->LTag = 1 ; root->left = pre; } if (pre != NULL && pre->right == NULL ) { pre->RTag = 1 ; pre->right = root; } pre = root; inThreading(root->right); } ThreadedTreeNode *createThreadedTree () { ThreadedTreeNode *root = createNode(1 ); root->left = createNode(2 ); root->right = createNode(3 ); root->left->left = createNode(4 ); root->left->right = createNode(5 ); pre = NULL ; inThreading(root); return root; } void inOrderTraverse (ThreadedTreeNode *root) { ThreadedTreeNode *current = root; while (current != NULL ) { while (current->LTag == 0 ) { current = current->left; } printf ("%d " , current->data); while (current->RTag == 1 ) { current = current->right; printf ("%d " , current->data); } current = current->right; } } int main () { ThreadedTreeNode *root = createThreadedTree(); printf ("中序遍历线索二叉树:" ); inOrderTraverse(root); return 0 ; }
其实和三种遍历一样,改改遍历顺序就好了
森林与树与二叉树的转换 感觉没啥用,自己去看ppt 技巧:
将树转换成二叉树 :兄弟相连留长子
二叉树变树:左孩右右连双亲,去掉原来右孩线
森林变二叉树:树变二叉根相连
二叉树变森林:去掉全部右孩线,孤立二叉再还原 // 主要是树和森林怎么变 代码实现巨麻烦,我就贴一个看看就好了1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 #include <stdio.h> #include <stdlib.h> typedef struct NTreeNode { char data; struct NTreeNode **children ; int child_count; } NTreeNode; typedef struct BinaryTreeNode { char data; struct BinaryTreeNode *left ; struct BinaryTreeNode *right ; } BinaryTreeNode; NTreeNode* createNTreeNode (char data, int child_count) { NTreeNode* node = (NTreeNode*)malloc (sizeof (NTreeNode)); node->data = data; node->child_count = child_count; node->children = (NTreeNode**)malloc (child_count * sizeof (NTreeNode*)); return node; } BinaryTreeNode* createBinaryTreeNode (char data) { BinaryTreeNode* node = (BinaryTreeNode*)malloc (sizeof (BinaryTreeNode)); node->data = data; node->left = node->right = NULL ; return node; } BinaryTreeNode* NTreeToBinaryTree (NTreeNode* nNode) { if (nNode == NULL ) return NULL ; BinaryTreeNode* bNode = createBinaryTreeNode(nNode->data); if (nNode->child_count > 0 ) { bNode->left = NTreeToBinaryTree(nNode->children[0 ]); } BinaryTreeNode* current = bNode->left; for (int i = 1 ; i < nNode->child_count; i++) { if (current == NULL ) { current = NTreeToBinaryTree(nNode->children[i]); bNode->left = current; } else { current->right = NTreeToBinaryTree(nNode->children[i]); current = current->right; } } return bNode; } NTreeNode* BinaryTreeToNTree (BinaryTreeNode* bNode) { if (bNode == NULL ) return NULL ; NTreeNode* nNode = createNTreeNode(bNode->data, 0 ); if (bNode->left != NULL ) { NTreeNode* leftChild = BinaryTreeToNTree(bNode->left); nNode->children = (NTreeNode**)realloc (nNode->children, ++(nNode->child_count) * sizeof (NTreeNode*)); nNode->children[nNode->child_count - 1 ] = leftChild; } BinaryTreeNode* sibling = bNode->right; while (sibling != NULL ) { NTreeNode* rightSibling = BinaryTreeToNTree(sibling); nNode->children = (NTreeNode**)realloc (nNode->children, ++(nNode->child_count) * sizeof (NTreeNode*)); nNode->children[nNode->child_count - 1 ] = rightSibling; sibling = sibling->right; } return nNode; } void printBinaryTreePreorder (BinaryTreeNode* root) { if (root == NULL ) return ; printf ("%c " , root->data); printBinaryTreePreorder(root->left); printBinaryTreePreorder(root->right); } void printNTree (NTreeNode* root) { if (root == NULL ) return ; printf ("%c " , root->data); for (int i = 0 ; i < root->child_count; i++) { printNTree(root->children[i]); } } int main () { NTreeNode* nRoot = createNTreeNode('A' , 3 ); nRoot->children[0 ] = createNTreeNode('B' , 2 ); nRoot->children[1 ] = createNTreeNode('C' , 0 ); nRoot->children[2 ] = createNTreeNode('D' , 0 ); nRoot->children[0 ]->children[0 ] = createNTreeNode('E' , 0 ); nRoot->children[0 ]->children[1 ] = createNTreeNode('F' , 0 ); BinaryTreeNode* bRoot = NTreeToBinaryTree(nRoot); printf ("Binary Tree (Preorder): " ); printBinaryTreePreorder(bRoot); printf ("\n" ); NTreeNode* newNRoot = BinaryTreeToNTree(bRoot); printf ("N Tree: " ); printNTree(newNRoot); printf ("\n" ); return 0 ; }
图 基本概念 图: G = (V, E) V: 顶点(Vertex)的有穷非空集合; 顶点表示数据元素 E: 边(Edge,或称弧Arc)的有穷集合; 边表示数据元素之间的关系
G1=<V1,E1> V1={v1 v2,v3,v4} E1={<v1,v2 >,<v1,v3 >,<v3,v4 >,<v4,v1 >}
无向图(Directed Graph, or Digraph): 每条边都是无方向的;
有向图(Undirected Graph, or Undigraph): 每条边都是有方向的。
完全图(Completed Graph): 任意两个点都有一条边相连
无向完全图:有n(n-1)/2条边(图中每个顶点和其余n-1个顶点都有边相连)的无向图为完全图。
有向完全图:有n(n-1)条边(图中每个顶点和其余n-1个顶点都有弧相连)的有向图为有向完全图。
稀疏图(Sparse Graph): 有很少边或弧的图(e < n log n)
稠密图(Dense Graph): 有较多边或弧的图
邻接: 有边/弧相连的两个顶点之间的关系
无向图: 存在边(v, v’), 称作v与v’互为邻接点(Adjacent),即v与v’相邻接; 边(v, v’)依附于(Incident)顶点v和v’ , 或者说(v, v’)与顶点v和v’ 相关联
有向图: 存在边< v, v’> ,则称顶点v邻接到顶点v’,顶点v’邻接自顶点v ,或者说弧< v, v’>与顶点v、v’相关联。
顶点的度(Degree):
对无向图而言,顶点v 的度是指和v相关联的边的数目,记作TD(v)
对有向图而言,顶点v的度是其入度和出度之和 • 入度(Indegree): 以v为终点的有向边的条数,记作ID(v) • 出度(Outdegree): 以v为始点的有向边的条数,记作OD(v) • TD(v) = ID(v) + OD(v)
路径(Path): 接续的边构成的顶点序列
对无向图而言,从顶点v到v’的路径是一个顶点序列(v=vi0, vi1, vi2, …,vim= v ’), 其中(vij-1, vij)∈E, 1≤j≤m。
对有向图而言,路径也是有向的,顶点序列应满足<vij-1, vij>∈E,1≤j≤m。
路径长度: 路径上边或弧的数目/权值之和
回路(环): 第一个顶点和最后一个顶点相同的路径
简单路径: 路径上顶点均不相同的路径
简单回路(简单环): 除路径起点和终点相同外,其余顶点均不相同的路径
连通图: 在无向图G=(V,{E})中,若从vi到vj有路径相通,则称顶点vi与vj是连通的。如果对于图中的任意两个顶点vi、vj∈V,vi与vj都是连通的,则称该无向图G为**连通图(Connected Graph)**。
强连通图:在有向图G=(V,{A})中,若对于每对顶点vi、vj∈V且vi≠vj,从vi到vj和vj到vi都有路径,则称该有向图为强连通图
连通分量 :无向图G中的极大连通子图称为G的连通分量(Connected Component)。 极大连通子图:该子图是G的连通子图,将G的任何不在该子 图中的顶点加入,子图不再连通。
强连通分量 :有向图的极大强连通子图称作有向图的强连通分量。 极大强连通子图:该子图是G的强连通子图,将D的任何不在子图中的顶点加入,子图不在是强连通的。
实现建图,dfs(深度优先遍历), bfs(广度优先遍历)
邻接表1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 #include <stdio.h> #include <stdlib.h> typedef struct Node { int vertex; struct Node * next ; } Node; typedef struct Graph { int V; Node** adjList; } Graph; Graph* createGraph (int V) { Graph* graph = (Graph*)malloc (sizeof (Graph)); graph->V = V; graph->adjList = (Node**)malloc (V * sizeof (Node*)); for (int i = 0 ; i < V; i++) { graph->adjList[i] = NULL ; } return graph; } void addEdge (Graph* graph, int src, int dest) { Node* newNode = (Node*)malloc (sizeof (Node)); newNode->vertex = dest; newNode->next = graph->adjList[src]; graph->adjList[src] = newNode; newNode = (Node*)malloc (sizeof (Node)); newNode->vertex = src; newNode->next = graph->adjList[dest]; graph->adjList[dest] = newNode; } void printGraph (Graph* graph) { for (int v = 0 ; v < graph->V; v++) { Node* temp = graph->adjList[v]; printf ("\nVertex %d: " , v); while (temp) { printf ("%d -> " , temp->vertex); temp = temp->next; } printf ("NULL\n" ); } } void BFS (Graph* graph, int start) { int * visited = (int *)malloc (graph->V * sizeof (int )); for (int i = 0 ; i < graph->V; i++) { visited[i] = 0 ; } int * queue = (int *)malloc (graph->V * sizeof (int )); int front = 0 , rear = 0 ; visited[start] = 1 ; queue [rear++] = start; printf ("\nBFS starting from vertex %d: " , start); while (front < rear) { int vertex = queue [front++]; printf ("%d " , vertex); Node* temp = graph->adjList[vertex]; while (temp) { if (!visited[temp->vertex]) { visited[temp->vertex] = 1 ; queue [rear++] = temp->vertex; } temp = temp->next; } } free (visited); free (queue ); } void DFSUtil (Graph* graph, int vertex, int * visited) { visited[vertex] = 1 ; printf ("%d " , vertex); Node* temp = graph->adjList[vertex]; while (temp) { if (!visited[temp->vertex]) { DFSUtil(graph, temp->vertex, visited); } temp = temp->next; } } void DFS (Graph* graph, int start) { int * visited = (int *)malloc (graph->V * sizeof (int )); for (int i = 0 ; i < graph->V; i++) { visited[i] = 0 ; } printf ("\nDFS starting from vertex %d: " , start); DFSUtil(graph, start, visited); free (visited); } int main () { Graph* graph = createGraph(5 ); addEdge(graph, 0 , 1 ); addEdge(graph, 0 , 4 ); addEdge(graph, 1 , 2 ); addEdge(graph, 1 , 3 ); addEdge(graph, 1 , 4 ); addEdge(graph, 2 , 3 ); addEdge(graph, 3 , 4 ); printGraph(graph); BFS(graph, 0 ); DFS(graph, 0 ); free (graph->adjList); free (graph); return 0 ; }
临界表的好处是空间小,运行时间短,但是不好写 -> 头节点 -> 节点1 -> 节点2 -> 节点3 -> NULL -> 新头节点(X) -> 头节点 -> 节点1 -> 节点2 -> 节点3 -> NULL
邻接矩阵 邻接矩阵是一种二维数组,其中矩阵中的元素表示图中节点之间的连接关系。如果图中存在边,则对应矩阵位置为1,否则为0。对于无向图,矩阵是对称的。 假设我们有一个图,包含 4 个节点,边的连接如下:
节点 0 与节点 1 和 2 有边。 节点 1 与节点 3 有边。 节点 2 与节点 3 有边。 这个图的邻接矩阵如下:
1 2 3 4 5 6 7 0 1 2 3 -------------- 0 | 0 1 1 0 1 | 1 0 0 1 2 | 1 0 0 1 3 | 0 1 1 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 #include <stdio.h> #include <stdlib.h> #define MAX_VERTICES 10 typedef struct Graph { int adjMatrix[MAX_VERTICES][MAX_VERTICES]; int V; } Graph; Graph* createGraph (int V) { Graph* graph = (Graph*)malloc (sizeof (Graph)); graph->V = V; for (int i = 0 ; i < V; i++) { for (int j = 0 ; j < V; j++) { graph->adjMatrix[i][j] = 0 ; } } return graph; } void addEdge (Graph* graph, int src, int dest) { graph->adjMatrix[src][dest] = 1 ; graph->adjMatrix[dest][src] = 1 ; } void printGraph (Graph* graph) { for (int i = 0 ; i < graph->V; i++) { for (int j = 0 ; j < graph->V; j++) { printf ("%d " , graph->adjMatrix[i][j]); } printf ("\n" ); } } void BFS (Graph* graph, int start) { int visited[MAX_VERTICES] = {0 }; int queue [MAX_VERTICES]; int front = 0 , rear = 0 ; visited[start] = 1 ; queue [rear++] = start; printf ("\nBFS starting from vertex %d: " , start); while (front < rear) { int vertex = queue [front++]; printf ("%d " , vertex); for (int i = 0 ; i < graph->V; i++) { if (graph->adjMatrix[vertex][i] == 1 && !visited[i]) { visited[i] = 1 ; queue [rear++] = i; } } } } void DFSUtil (Graph* graph, int vertex, int * visited) { visited[vertex] = 1 ; printf ("%d " , vertex); for (int i = 0 ; i < graph->V; i++) { if (graph->adjMatrix[vertex][i] == 1 && !visited[i]) { DFSUtil(graph, i, visited); } } } void DFS (Graph* graph, int start) { int visited[MAX_VERTICES] = {0 }; printf ("\nDFS starting from vertex %d: " , start); DFSUtil(graph, start, visited); } int main () { Graph* graph = createGraph(4 ); addEdge(graph, 0 , 1 ); addEdge(graph, 0 , 2 ); addEdge(graph, 1 , 3 ); addEdge(graph, 2 , 3 ); printf ("Graph's Adjacency Matrix:\n" ); printGraph(graph); BFS(graph, 0 ); DFS(graph, 0 ); free (graph); return 0 ; }
深度优先搜索 (DFS) DFS 是一种“深度”优先的遍历方式,意味着它会尽可能深地访问一个节点的子节点,直到无法继续深度访问为止,然后回溯并访问其他未被访问的节点。
工作原理: 从一个起始节点开始,访问这个节点。 然后递归地访问所有与当前节点相邻且未被访问的节点,直到达到没有未访问的相邻节点为止。 当无法继续深度访问时,回到上一个节点,继续访问它的其他邻居。 特点: 递归或栈:DFS 通常使用递归或者显式栈来实现,依靠栈的后进先出(LIFO)特性来实现回溯。 深入探索:优先沿着一条路径探索下去,直到遇到死胡同,再回溯。 应用:用于求解连通分量、拓扑排序、寻找路径等问题。
0 -> 1 -> 3 -> 2 -> 4
1 2 3 4 5 6 7 8 9 10 11 void DFS (int graph[][5 ], int node, int visited[]) { visited[node] = 1 ; printf ("%d " , node); for (int i = 0 ; i < 5 ; i++) { if (graph[node][i] == 1 && !visited[i]) { DFS(graph, i, visited); } } }
BFS 是一种“宽度”优先的遍历方式,意味着它会先访问所有与起始节点直接相邻的节点,然后逐层扩展,访问与当前节点相邻的节点。
工作原理: 从一个起始节点开始,访问这个节点。 然后访问所有与当前节点直接相邻的未访问节点,并将它们加入队列。 接着从队列中依次取出节点,继续访问它们的相邻节点,直到所有节点都被访问过。 特点: 队列:BFS 使用队列来实现,依靠队列的先进先出(FIFO)特性来保证按层次遍历。 层次遍历:优先访问与当前节点在同一层的节点,然后再向下访问下一层节点。 应用:用于最短路径查找、图的层次遍历、连通性等问题。 同样的例子
0 -> 1 -> 2 -> 3 -> 4 看了代码就理解了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void BFS (int graph[][5 ], int start, int visited[]) { int queue [5 ], front = 0 , rear = 0 ; visited[start] = 1 ; queue [rear++] = start; while (front < rear) { int node = queue [front++]; printf ("%d " , node); for (int i = 0 ; i < 5 ; i++) { if (graph[node][i] == 1 && !visited[i]) { visited[i] = 1 ; queue [rear++] = i; } } } }
图的连通性问题,最小生成树 Prim算法(基于贪心去实现的) Kruskal算法(基于并查集去实现的)
prim 它的基本思想是从图中的某个起始节点出发,逐步选择最小的边,扩展生成树,直到所有节点都被包含在内。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <stdio.h> #include <limits.h> #define MAX_VERTICES 10 #define INF INT_MAX int minKey (int key[], int mstSet[], int vertices) { int min = INF, min_index; for (int v = 0 ; v < vertices; v++) { if (mstSet[v] == 0 && key[v] < min) { min = key[v]; min_index = v; } } return min_index; } void prim (int graph[MAX_VERTICES][MAX_VERTICES], int vertices) { int parent[MAX_VERTICES]; int key[MAX_VERTICES]; int mstSet[MAX_VERTICES]; for (int i = 0 ; i < vertices; i++) { key[i] = INF; mstSet[i] = 0 ; } key[0 ] = 0 ; parent[0 ] = -1 ; for (int count = 0 ; count < vertices - 1 ; count++) { int u = minKey(key, mstSet, vertices); mstSet[u] = 1 ; for (int v = 0 ; v < vertices; v++) { if (graph[u][v] && mstSet[v] == 0 && graph[u][v] < key[v]) { parent[v] = u; key[v] = graph[u][v]; } } } printf ("Edge \tWeight\n" ); for (int i = 1 ; i < vertices; i++) { printf ("%d - %d \t%d\n" , parent[i], i, graph[i][parent[i]]); } } int main () { int graph[MAX_VERTICES][MAX_VERTICES] = { {0 , 2 , 0 , 6 , 0 , 0 }, {2 , 0 , 3 , 8 , 5 , 0 }, {0 , 3 , 0 , 0 , 7 , 0 }, {6 , 8 , 0 , 0 , 9 , 4 }, {0 , 5 , 7 , 9 , 0 , 3 }, {0 , 0 , 0 , 4 , 3 , 0 } }; int vertices = 6 ; prim(graph, vertices); return 0 ; }
Edge Weight 0 - 1 2 1 - 2 3 0 - 3 6 3 - 4 5 4 - 5 3
Kruskal算法 Kruskal算法的步骤:
排序边:将图中的所有边按照权重从小到大进行排序。
初始化:每个顶点开始时是一个独立的集合,表示每个顶点都在自己的连通分量中。
遍历边:依次检查排序后的每一条边,如果边的两个端点属于不同的连通分量,就将它们连接起来,并合并这两个连通分量。
重复:直到生成树包含图中所有的顶点或者所有边都被处理完。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <stdio.h> #include <stdlib.h> #define MAX_VERTICES 10 #define INF 99999 typedef struct { int u, v, weight; } Edge; int compareEdges (const void *a, const void *b) { return ((Edge *)a)->weight - ((Edge *)b)->weight; } int findParent (int parent[], int x) { while (parent[x] != x) { x = parent[x]; } return x; } void kruskal (int graph[MAX_VERTICES][MAX_VERTICES], int vertices) { Edge edges[MAX_VERTICES * (MAX_VERTICES - 1 ) / 2 ]; int edgeCount = 0 ; for (int i = 0 ; i < vertices; i++) { for (int j = i + 1 ; j < vertices; j++) { if (graph[i][j] != 0 && graph[i][j] != INF) { edges[edgeCount].u = i; edges[edgeCount].v = j; edges[edgeCount].weight = graph[i][j]; edgeCount++; } } } qsort(edges, edgeCount, sizeof (Edge), compareEdges); int parent[MAX_VERTICES]; for (int i = 0 ; i < vertices; i++) { parent[i] = i; } int mstWeight = 0 ; printf ("Edge \tWeight\n" ); for (int i = 0 ; i < edgeCount; i++) { int u = edges[i].u; int v = edges[i].v; int rootU = findParent(parent, u); int rootV = findParent(parent, v); if (rootU != rootV) { printf ("%d - %d \t%d\n" , u, v, edges[i].weight); mstWeight += edges[i].weight; parent[rootU] = rootV; } } printf ("Minimum spanning tree weight: %d\n" , mstWeight); } int main () { int graph[MAX_VERTICES][MAX_VERTICES] = { {0 , 2 , 0 , 6 , 0 , 0 }, {2 , 0 , 3 , 8 , 5 , 0 }, {0 , 3 , 0 , 0 , 7 , 0 }, {6 , 8 , 0 , 0 , 9 , 4 }, {0 , 5 , 7 , 9 , 0 , 3 }, {0 , 0 , 0 , 4 , 3 , 0 } }; int vertices = 6 ; kruskal(graph, vertices); return 0 ; }
最短路 问题抽象:在有向网中A点(源点)到达B点(终点)的多条路径中,寻找一条各边权值之和最小的路径,即最短路径。
单源最短路 dijstra Dijkstra算法的步骤: 初始化: 将起点的最短路径初始化为0,其他所有节点的最短路径初始化为无穷大(表示尚未确定最短路径) 使用一个布尔数组(或集合)S,记录已经找到最短路径的节点 选择当前最短的节点: 在未被加入集合S的节点中,选择当前最短路径值最小的节点u 更新邻接节点的最短路径: 对于节点u的每个邻接节点v,如果通过u到v的路径更短,就更新v的最短路径 重复步骤2和3,直到所有节点的最短路径都被找到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <stdio.h> #include <limits.h> #define MAX_VERTICES 10 #define INF INT_MAX int minDistance (int dist[], int sptSet[], int vertices) { int min = INF, min_index; for (int v = 0 ; v < vertices; v++) { if (sptSet[v] == 0 && dist[v] < min) { min = dist[v]; min_index = v; } } return min_index; } void dijkstra (int graph[MAX_VERTICES][MAX_VERTICES], int vertices, int start) { int dist[MAX_VERTICES]; int sptSet[MAX_VERTICES]; for (int i = 0 ; i < vertices; i++) { dist[i] = INF; sptSet[i] = 0 ; } dist[start] = 0 ; for (int count = 0 ; count < vertices - 1 ; count++) { int u = minDistance(dist, sptSet, vertices); sptSet[u] = 1 ; for (int v = 0 ; v < vertices; v++) { if (graph[u][v] != 0 && sptSet[v] == 0 && dist[u] != INF && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } printf ("Vertex\tDistance from Source\n" ); for (int i = 0 ; i < vertices; i++) { printf ("%d \t%d\n" , i, dist[i]); } } int main () { int graph[MAX_VERTICES][MAX_VERTICES] = { {0 , 10 , 0 , 0 , 0 , 0 }, {10 , 0 , 5 , 0 , 0 , 0 }, {0 , 5 , 0 , 2 , 3 , 0 }, {0 , 0 , 2 , 0 , 3 , 9 }, {0 , 0 , 3 , 3 , 0 , 1 }, {0 , 0 , 0 , 9 , 1 , 0 } }; int vertices = 6 ; int start = 0 ; dijkstra(graph, vertices, start); return 0 ; }
多源最短路 floyd 当然也可以跑n遍dijstra floyd其实就是简单的动态规划
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <stdio.h> #include <limits.h> #define MAX_VERTICES 10 #define INF INT_MAX void floydWarshall (int graph[MAX_VERTICES][MAX_VERTICES], int vertices) { int dist[MAX_VERTICES][MAX_VERTICES]; for (int i = 0 ; i < vertices; i++) { for (int j = 0 ; j < vertices; j++) { if (i == j) { dist[i][j] = 0 ; } else if (graph[i][j] == 0 ) { dist[i][j] = INF; } else { dist[i][j] = graph[i][j]; } } } for (int k = 0 ; k < vertices; k++) { for (int i = 0 ; i < vertices; i++) { for (int j = 0 ; j < vertices; j++) { if (dist[i][k] != INF && dist[k][j] != INF && dist[i][j] > dist[i][k] + dist[k][j]) { dist[i][j] = dist[i][k] + dist[k][j]; } } } } printf ("Shortest distances between every pair of vertices:\n" ); for (int i = 0 ; i < vertices; i++) { for (int j = 0 ; j < vertices; j++) { if (dist[i][j] == INF) { printf ("INF\t" ); } else { printf ("%d\t" , dist[i][j]); } } printf ("\n" ); } } int main () { int graph[MAX_VERTICES][MAX_VERTICES] = { {0 , 3 , INF, INF, INF, INF}, {3 , 0 , 1 , INF, INF, INF}, {INF, 1 , 0 , 7 , INF, 2 }, {INF, INF, 7 , 0 , 2 , 3 }, {INF, INF, INF, 2 , 0 , 3 }, {INF, INF, 2 , 3 , 3 , 0 } }; int vertices = 6 ; floydWarshall(graph, vertices); return 0 ; }
拓扑排序 思想: 计算每个顶点的入度。 将所有入度为0的顶点加入队列(或集合),它们没有任何依赖关系。 每次从队列中取出一个顶点,将其加入拓扑排序中,并将其邻接点的入度减1。如果某个邻接点的入度变为0,加入队列。 重复步骤3,直到所有顶点都被处理。如果图中存在环,说明无法进行拓扑排序。
步骤: 初始化一个队列,将所有入度为0的节点放入队列。 当队列不为空时: 从队列中取出一个节点。 将这个节点加入拓扑排序结果中。 遍历该节点的所有邻接节点,减少它们的入度。如果某个邻接节点的入度变为0,将其加入队列。 如果最终拓扑排序的节点数小于图中的总节点数,说明图中存在环,无法进行拓扑排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <stdio.h> #include <stdlib.h> #define MAX_VERTICES 10 typedef struct { int vertices; int adj[MAX_VERTICES][MAX_VERTICES]; } Graph; void initGraph (Graph *g, int vertices) { g->vertices = vertices; for (int i = 0 ; i < vertices; i++) { for (int j = 0 ; j < vertices; j++) { g->adj[i][j] = 0 ; } } } void addEdge (Graph *g, int u, int v) { g->adj[u][v] = 1 ; } int topologicalSort (Graph *g) { int inDegree[MAX_VERTICES] = {0 }; int queue [MAX_VERTICES], front = 0 , rear = 0 ; int result[MAX_VERTICES]; int idx = 0 ; for (int i = 0 ; i < g->vertices; i++) { for (int j = 0 ; j < g->vertices; j++) { if (g->adj[i][j] == 1 ) { inDegree[j]++; } } } for (int i = 0 ; i < g->vertices; i++) { if (inDegree[i] == 0 ) { queue [rear++] = i; } } while (front < rear) { int u = queue [front++]; result[idx++] = u; for (int v = 0 ; v < g->vertices; v++) { if (g->adj[u][v] == 1 ) { inDegree[v]--; if (inDegree[v] == 0 ) { queue [rear++] = v; } } } } if (idx != g->vertices) { printf ("图中有环,无法进行拓扑排序。\n" ); return 0 ; } printf ("拓扑排序结果:\n" ); for (int i = 0 ; i < g->vertices; i++) { printf ("%d " , result[i]); } printf ("\n" ); return 1 ; } int main () { Graph g; initGraph(&g, 6 ); addEdge(&g, 5 , 2 ); addEdge(&g, 5 , 0 ); addEdge(&g, 4 , 0 ); addEdge(&g, 4 , 1 ); addEdge(&g, 2 , 3 ); addEdge(&g, 3 , 1 ); topologicalSort(&g); return 0 ; }
1 2 3 4 5 6 5 4 / \ / \ 2 0 0 1 \ / 3
拓扑排序结果:5 4 2 3 0 1
查找 线性表
顺序查找(线性查找)
for循环啊
折半查找(二分查找) 初始范围:设定查找区间的左右边界,初始时为数组的开始和结束索引。 中间值:每次通过计算区间的中间元素(mid = (low + high) / 2),与目标值进行比较。 缩小范围: 如果中间值等于目标值,则查找成功。 如果目标值小于中间值,则将查找区间缩小为左半部分。 如果目标值大于中间值,则将查找区间缩小为右半部分。 继续查找:重复上述步骤,直到找到目标值或查找范围为空
一定要保证是升序或者降序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <stdio.h> int binarySearch (int arr[], int size, int target) { int low = 0 ; int high = size - 1 ; while (low <= high) { int mid = low + (high - low) / 2 ; if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { low = mid + 1 ; } else { high = mid - 1 ; } } return -1 ; } int main () { int arr[] = {1 , 3 , 5 , 7 , 9 , 11 , 13 , 15 }; int size = sizeof (arr) / sizeof (arr[0 ]); int target = 7 ; int result = binarySearch(arr, size, target); if (result != -1 ) { printf ("目标元素 %d 在数组中的位置是 %d\n" , target, result); } else { printf ("目标元素 %d 不在数组中\n" , target); } return 0 ; }
分块查找(索引顺序查找) 基本思路: – (1)先让数据分块有序,即分成若干子表, 要求每个子表中的数据元素值都比后一块 中的数值小(但子表内部未必有序)
(2)然后将各子表中的最大关键字构成 一个索引表,表中还要包含每个子表的起 始地址。 分块 + 二分可以优化1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <stdio.h> #include <math.h> int binarySearch (int arr[], int low, int high, int target) { while (low <= high) { int mid = low + (high - low) / 2 ; if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { low = mid + 1 ; } else { high = mid - 1 ; } } return -1 ; } int blockSearch (int arr[], int size, int target) { int blockSize = (int )sqrt (size); int blockStart = 0 ; while (blockStart < size && arr[blockStart + blockSize - 1 ] < target) { blockStart += blockSize; } return binarySearch(arr, blockStart, blockStart + blockSize - 1 , target); } int main () { int arr[] = {1 , 3 , 5 , 7 , 9 , 11 , 13 , 15 , 17 , 19 , 21 , 23 , 25 , 27 }; int size = sizeof (arr) / sizeof (arr[0 ]); int target = 19 ; int result = blockSearch(arr, size, target); if (result != -1 ) { printf ("目标元素 %d 在数组中的位置是 %d\n" , target, result); } else { printf ("目标元素 %d 不在数组中\n" , target); } return 0 ; }
二叉排序树 二叉排序树的基本操作:
查找(Search):根据树的性质,从根节点开始,比较目标值与当前节点的值:
如果目标值小于当前节点的值,则在左子树中查找。 如果目标值大于当前节点的值,则在右子树中查找。 如果目标值等于当前节点的值,则查找成功,返回该节点。 2. 插入(Insert):从根节点开始,逐层比较待插入值与当前节点的值,找到合适的位置后插入新的节点。
删除(Delete):删除节点有三种情况:
删除的节点没有子节点:直接删除该节点。 删除的节点有一个子节点:用该节点的唯一子节点代替该节点。 删除的节点有两个子节点:找到该节点的右子树中的最小节点(即中序遍历的下一个节点),用该最小节点的值替代当前节点的值,再删除该最小节点。 4. 遍历(Traversal):常用的遍历方法有:
前序遍历(Pre-order Traversal):根节点 -> 左子树 -> 右子树。 中序遍历(In-order Traversal):左子树 -> 根节点 -> 右子树。 后序遍历(Post-order Traversal):左子树 -> 右子树 -> 根节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * left ; struct Node * right ; } Node; Node* createNode (int data) { Node* newNode = (Node*)malloc (sizeof (Node)); newNode->data = data; newNode->left = newNode->right = NULL ; return newNode; } Node* insert (Node* root, int data) { if (root == NULL ) { return createNode(data); } if (data < root->data) { root->left = insert(root->left, data); } else { root->right = insert(root->right, data); } return root; } Node* search (Node* root, int data) { if (root == NULL || root->data == data) { return root; } if (data < root->data) { return search(root->left, data); } else { return search(root->right, data); } } Node* deleteNode (Node* root, int data) { if (root == NULL ) { return root; } if (data < root->data) { root->left = deleteNode(root->left, data); } else if (data > root->data) { root->right = deleteNode(root->right, data); } else { if (root->left == NULL && root->right == NULL ) { free (root); root = NULL ; } else if (root->left == NULL ) { Node* temp = root; root = root->right; free (temp); } else if (root->right == NULL ) { Node* temp = root; root = root->left; free (temp); } else { Node* temp = root->right; while (temp && temp->left != NULL ) { temp = temp->left; } root->data = temp->data; root->right = deleteNode(root->right, temp->data); } } return root; } void inorderTraversal (Node* root) { if (root == NULL ) { return ; } inorderTraversal(root->left); printf ("%d " , root->data); inorderTraversal(root->right); } int main () { Node* root = NULL ; root = insert(root, 50 ); root = insert(root, 30 ); root = insert(root, 20 ); root = insert(root, 40 ); root = insert(root, 70 ); root = insert(root, 60 ); root = insert(root, 80 ); printf ("中序遍历结果:" ); inorderTraversal(root); printf ("\n" ); Node* result = search(root, 40 ); if (result != NULL ) { printf ("找到了节点 %d\n" , result->data); } else { printf ("节点未找到\n" ); } root = deleteNode(root, 20 ); root = deleteNode(root, 30 ); root = deleteNode(root, 50 ); printf ("删除后中序遍历结果:" ); inorderTraversal(root); printf ("\n" ); return 0 ; }
问题是可能退化成链,所以这里需要引入平衡树的概念 课本中学的是AVL树 就四个操作 右旋
1 2 3 4 5 6 y x / \ / \ x T4 Right Rotate (y) z y / \ - - - - - - - - - - - > / \ / \ z T3 T1 T2 T3 T4
左旋
1 2 3 4 5 x y / \ / \ T1 y Left Rotate (x) x z / \ - - - - - - - - - - > / \ / \ T2 z T1 T2 T3 T4
左-右旋
1 2 3 4 5 6 7 8 z x z / \ / \ / \ x T4 Left Rotate (z) y T4 Right Rotate (x) x y / \ - - - - - - - - - - > / \ / \ / \ T1 y T1 x T1 T2 T3 T4 / \ / \ T2 T3 T2 T3
右-左旋
1 2 3 4 5 6 7 8 x z x / \ / \ / \ T1 z Right Rotate (x) y T2 Left Rotate (z) y z / \ - - - - - - - - - > / \ / \ / \ y T3 T1 x T1 T2 T3 T4 / \ / \ T2 T3 T3 T4
贴个代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * left ; struct Node * right ; int height; } Node; int height (Node* node) { if (node == NULL ) { return 0 ; } return node->height; } int getBalanceFactor (Node* node) { if (node == NULL ) { return 0 ; } return height(node->left) - height(node->right); } Node* createNode (int data) { Node* newNode = (Node*)malloc (sizeof (Node)); newNode->data = data; newNode->left = newNode->right = NULL ; newNode->height = 1 ; return newNode; } Node* rightRotate (Node* y) { Node* x = y->left; Node* T2 = x->right; x->right = y; y->left = T2; y->height = 1 + (height(y->left) > height(y->right) ? height(y->left) : height(y->right)); x->height = 1 + (height(x->left) > height(x->right) ? height(x->left) : height(x->right)); return x; } Node* leftRotate (Node* x) { Node* y = x->right; Node* T2 = y->left; y->left = x; x->right = T2; x->height = 1 + (height(x->left) > height(x->right) ? height(x->left) : height(x->right)); y->height = 1 + (height(y->left) > height(y->right) ? height(y->left) : height(y->right)); return y; } Node* insert (Node* node, int data) { if (node == NULL ) { return createNode(data); } if (data < node->data) { node->left = insert(node->left, data); } else if (data > node->data) { node->right = insert(node->right, data); } else { return node; } node->height = 1 + (height(node->left) > height(node->right) ? height(node->left) : height(node->right)); int balance = getBalanceFactor(node); if (balance > 1 && data < node->left->data) { return rightRotate(node); } if (balance < -1 && data > node->right->data) { return leftRotate(node); } if (balance > 1 && data > node->left->data) { node->left = leftRotate(node->left); return rightRotate(node); }
哈希表 重点是哈希冲突,简单来讲就是1 % 13 == 14 % 13, 两个数具有相同的哈希值
优化:
选择合适的质数:使用质数作为哈希表的大小可以显著减少冲突。如果选择的质数太小,可能会导致哈希表容量不足;如果太大,则可能浪费空间。
变种哈希函数:为了进一步减少冲突,可以使用更多的哈希函数组合,或使用一些其他哈希技术,如 乘法散列法、位运算、多项式哈希法 等 会除留余数法就好了
有冲突时就去寻找下一个空的哈希地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include <stdio.h> #include <stdlib.h> #define TABLE_SIZE 11 typedef struct HashTable { int *table; int size; } HashTable; HashTable* createTable (int size) { HashTable* hashTable = (HashTable*)malloc (sizeof (HashTable)); hashTable->size = size; hashTable->table = (int *)malloc (sizeof (int ) * size); for (int i = 0 ; i < size; i++) { hashTable->table[i] = -1 ; } return hashTable; } int hash (int key, int size) { return key % size; } void insert (HashTable* hashTable, int key) { int index = hash(key, hashTable->size); while (hashTable->table[index] != -1 ) { index = (index + 1 ) % hashTable->size; } hashTable->table[index] = key; } int search (HashTable* hashTable, int key) { int index = hash(key, hashTable->size); while (hashTable->table[index] != -1 ) { if (hashTable->table[index] == key) { return 1 ; } index = (index + 1 ) % hashTable->size; } return 0 ; } void printTable (HashTable* hashTable) { for (int i = 0 ; i < hashTable->size; i++) { if (hashTable->table[i] != -1 ) { printf ("%d " , hashTable->table[i]); } else { printf ("X " ); } } printf ("\n" ); } int main () { HashTable* hashTable = createTable(TABLE_SIZE); insert(hashTable, 23 ); insert(hashTable, 45 ); insert(hashTable, 67 ); insert(hashTable, 12 ); printTable(hashTable); printf ("Search 45: %d\n" , search(hashTable, 45 )); printf ("Search 100: %d\n" , search(hashTable, 100 )); return 0 ; }
排序 排序的定义不需要介绍了吧 有几个比较简单的直接看代码就好了
插入排序 每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void insertionSort (int arr[], int n) { int i, key, j; for (i = 1 ; i < n; i++) { key = arr[i]; j = i - 1 ; while (j >= 0 && arr[j] > key) { arr[j + 1 ] = arr[j]; j = j - 1 ; } arr[j + 1 ] = key; } }
优化:二分插入排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <stdio.h> int binarySearch (int arr[], int left, int right, int key) { while (left <= right) { int mid = left + (right - left) / 2 ; if (arr[mid] == key) { return mid + 1 ; } if (arr[mid] > key) { right = mid - 1 ; } else { left = mid + 1 ; } } return left; } void binaryInsertionSort (int arr[], int n) { int i, key, j, insertPos; for (i = 1 ; i < n; i++) { key = arr[i]; insertPos = binarySearch(arr, 0 , i - 1 , key); for (j = i - 1 ; j >= insertPos; j--) { arr[j + 1 ] = arr[j]; } arr[insertPos] = key; } } void printArray (int arr[], int n) { for (int i = 0 ; i < n; i++) { printf ("%d " , arr[i]); } printf ("\n" ); } int main () { int arr[] = {12 , 11 , 13 , 5 , 6 }; int n = sizeof (arr) / sizeof (arr[0 ]); printf ("Original array: \n" ); printArray(arr, n); binaryInsertionSort(arr, n); printf ("Sorted array: \n" ); printArray(arr, n); return 0 ; }
不过只是优化了常数,因为本身的交换缺陷,无法更快
交换排序 两两比较,如果发生逆序则交换,直到所有记录都排好序为止
起泡排序(Bubble Sorting) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void bubbleSort (int arr[], int n) { int i, j, temp; int swapped; for (i = 0 ; i < n - 1 ; i++) { swapped = 0 ; for (j = 0 ; j < n - i - 1 ; j++) { if (arr[j] > arr[j + 1 ]) { temp = arr[j]; arr[j] = arr[j + 1 ]; arr[j + 1 ] = temp; swapped = 1 ; } } if (!swapped) { break ; } } }
快速排序 【三分钟学会快速排序-哔哩哔哩】 https://b23.tv/7IwWAC3 还行,很有必要学
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 int partition (int arr[], int low, int high) { int pivot = arr[high]; int i = low - 1 ; for (int j = low; j <= high - 1 ; j++) { if (arr[j] < pivot) { i++; int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } int temp = arr[i + 1 ]; arr[i + 1 ] = arr[high]; arr[high] = temp; return i + 1 ; } void quickSort (int arr[], int low, int high) { if (low < high) { int pi = partition(arr, low, high); quickSort(arr, low, pi - 1 ); quickSort(arr, pi + 1 , high); } }
选择排序 首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第1个记录交换 2.再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第2个记录交换 3.重复上述操作,共进行n-1趟排序后,排序结束
1 2 3 4 5 6 7 8 9 10 void selectionSort (int arr[], int n) { for (int i = 0 ; i < n - 1 ; i++) { int minIndex = i; for (int j = i + 1 ; j < n; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } } }
树形选择排序 基本思想:首先对n个记录的关键字进行两两比较,然后在其中┎ n/2 ┐个较小者之间再进行两两比较,如此重复,直至选出最小关键字的记录为止。 实现:这个过程可用一棵有n个叶子结点的完全二叉树表示树表示 建树是nlogn,每次选择是logn,所以总的是nlogn,常数项比较大
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * left ; struct Node * right ; } Node; Node* newNode (int data) { Node* node = (Node*)malloc (sizeof (Node)); node->data = data; node->left = node->right = NULL ; return node; } Node* insert (Node* root, int data) { if (root == NULL ) { return newNode(data); } if (data < root->data) { root->left = insert(root->left, data); } else { root->right = insert(root->right, data); } return root; } void inorderTraversal (Node* root) { if (root != NULL ) { inorderTraversal(root->left); printf ("%d " , root->data); inorderTraversal(root->right); } } Node* findMin (Node* root) { while (root && root->left != NULL ) { root = root->left; } return root; } Node* deleteMin (Node* root) { if (root == NULL ) return NULL ; if (root->left == NULL ) { Node* rightChild = root->right; free (root); return rightChild; } root->left = deleteMin(root->left); return root; } void treeSelectionSort (int arr[], int n) { Node* root = NULL ; for (int i = 0 ; i < n; i++) { root = insert(root, arr[i]); } for (int i = 0 ; i < n; i++) { Node* minNode = findMin(root); arr[i] = minNode->data; root = deleteMin(root); } } int main () { int arr[] = {64 , 25 , 12 , 22 , 11 }; int n = sizeof (arr) / sizeof (arr[0 ]); printf ("Original array: \n" ); for (int i = 0 ; i < n; i++) { printf ("%d " , arr[i]); } printf ("\n" ); treeSelectionSort(arr, n); printf ("Sorted array: \n" ); for (int i = 0 ; i < n; i++) { printf ("%d " , arr[i]); } printf ("\n" ); return 0 ; }
堆排序 分为大根堆和小根堆,就是字面意思 这里建议看一下课件,描述不如图片直观
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include <stdio.h> void swap (int *x, int *y) { int temp = *x; *x = *y; *y = temp; } void heapify (int arr[], int n, int i) { int largest = i; int left = 2 * i + 1 ; int right = 2 * i + 2 ; if (left < n && arr[left] > arr[largest]) { largest = left; } if (right < n && arr[right] > arr[largest]) { largest = right; } if (largest != i) { swap(&arr[i], &arr[largest]); heapify(arr, n, largest); } } void heapSort (int arr[], int n) { for (int i = n / 2 - 1 ; i >= 0 ; i--) { heapify(arr, n, i); } for (int i = n - 1 ; i > 0 ; i--) { swap(&arr[0 ], &arr[i]); heapify(arr, i, 0 ); } } void printArray (int arr[], int size) { for (int i = 0 ; i < size; i++) { printf ("%d " , arr[i]); } printf ("\n" ); } int main () { int arr[] = {12 , 11 , 13 , 5 , 6 , 7 }; int n = sizeof (arr) / sizeof (arr[0 ]); printf ("Original array: \n" ); printArray(arr, n); heapSort(arr, n); printf ("Sorted array: \n" ); printArray(arr, n); return 0 ; }
归并排序 递归的将两个已经排序的序列合并成一个序列 还是建议看课件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #include <stdio.h> #include <stdlib.h> void merge (int arr[], int left, int mid, int right) { int n1 = mid - left + 1 ; int n2 = right - mid; int L[n1], R[n2]; for (int i = 0 ; i < n1; i++) L[i] = arr[left + i]; for (int i = 0 ; i < n2; i++) R[i] = arr[mid + 1 + i]; int i = 0 , j = 0 , k = left; while (i < n1 && j < n2) { if (L[i] <= R[j]) { arr[k] = L[i]; i++; } else { arr[k] = R[j]; j++; } k++; } while (i < n1) { arr[k] = L[i]; i++; k++; } while (j < n2) { arr[k] = R[j]; j++; k++; } } void mergeSort (int arr[], int left, int right) { if (left < right) { int mid = left + (right - left) / 2 ; mergeSort(arr, left, mid); mergeSort(arr, mid + 1 , right); merge(arr, left, mid, right); } } void printArray (int arr[], int size) { for (int i = 0 ; i < size; i++) { printf ("%d " , arr[i]); } printf ("\n" ); } int main () { int arr[] = {12 , 11 , 13 , 5 , 6 , 7 }; int n = sizeof (arr) / sizeof (arr[0 ]); printf ("Original array: \n" ); printArray(arr, n); mergeSort(arr, 0 , n - 1 ); printf ("Sorted array: \n" ); printArray(arr, n); return 0 ; }
完结撒花,建议配合ppt使用