Data_structures_BaseDataType
本文代码采用C语言编写,用到了部分Cpp语法,详见 C_NOTE 的Cpp引用部分
由于每种数据结构各种操作需要相互协同工作才能起作用,总体代码长度过长,不方便阅读,故此我决定将不同操作分开讲解,并在章节最后附上整体的代码 (其实就是合到一起)
时间复杂度&空间复杂度 时间复杂度(time complexity) 时间复杂度是指算法执行所需时间的量度,通常以输入规模 的函数形式表示。它反映了算法的运行时间如何随着输入规模的增加而变化。
:常数时间复杂度,与输入规模无关,执行时间固定。
:线性时间复杂度,时间与输入规模成正比,如遍历数组。
:对数时间复杂度,适合分治法(如二分查找)。
:平方时间复杂度,常见于嵌套循环
通常我们观察一段代码的运行次数来判断它的时间复杂度
1 2 3 4 def example (arr) : for i in range(len(arr)): for j in range(len(arr)): print(arr[i] + arr[j])
外层循环运行 次,内层循环也运行 次,因此时间复杂度为
空间复杂度(space complexity) 常见情况
:仅使用常数空间,通常为变量或指针的操作,如简单循环。
:需要存储所有输入元素的大小空间,如线性数组。
:需要矩阵或二维表结构的空间,如图论算法。
计算空间复杂度的步骤
识别固定存储 :如函数内的常量。
计算辅助结构 :例如数组、栈、队列。
加和取上限 :得到整体复杂度。
高效算法应在降低时间复杂度的前提下尽量减少空间开销
线性表 线性表(Linear List)是计算机科学中一种常见的数据结构,用来表示元素按顺序排列的一组数据。在线性表中,数据元素之间存在一对一的线性关系,即每个数据元素有且仅有一个直接前驱和一个直接后继(第一个和最后一个元素除外)。线性表可用于各种场景,包括队列、栈、链表等
顺序表(Sequence List) 顺序表 (Sequnce List) 使用一块连续的内存空间来存储线性表的数据元素。每个元素的存储位置可以通过一个下标直接访问,其本质是一个数组。
优点:
支持快速的随机访问,可以通过下标直接访问任何位置的元素。
空间利用率较高,不需要额外的存储空间。
缺点:
插入和删除元素的操作效率较低,涉及大量元素的移动。
顺序存储的大小固定,无法动态扩展。
1. 顺序表的静态定义 : 包含一个数组 和 一个用来记录当前顺序表长度的int变量
1 2 3 4 5 6 7 8 9 typedef int ElemType; typedef struct { ElemType data[MaxSize]; int length; }SqList;
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 bool ListInsert (SqList& L, int i, ElemType element) { int i_index = i - 1 ; int length_index = L.length - 1 ; if (i_index < 0 || i_index > length_index) { printf ("the position of i is illegal , change it to less than L.length\n" ); return false ; } if (length_index == MaxSize) { printf ("MaxSize cannot insert value\n" ); return false ; } L.length++; length_index = L.length - 1 ; for (int j = length_index; j >= i_index; j--) { L.data[j] = L.data[j - 1 ]; } L.data[i_index] = element; }
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 bool DelteElement_by_value (SqList& L, ElemType element) { int j = -1 ; for (int i = 0 ; i < L.length-1 ; i++) { if (element == L.data[i]) { j = i; break ; } } if (j == -1 ) { printf ("Element not found.\n" ); return false ; } for (int i = j; i < L.length - 1 ; i++) { L.data[i] = L.data[i + 1 ]; } L.length--; return true ; } bool DelteElement_by_position (SqList& L, int i) { int i_index = i - 1 ; int length_index = L.length - 1 ; if (i_index < 0 || i_index > length_index) { printf ("the position of i is illegal , change it to less than L.length\n" ); return false ; } for (int i = i_index; i < L.length - 1 ; i++) { L.data[i] = L.data[i + 1 ]; } L.length--; return true ; }
4.查找 : 直接通过下标访问目标位置元素
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 int SerchFirstPosition_by_value (SqList& L, ElemType element) { int j = -1 ; for (int i = 0 ; i < L.length - 1 ; i++) { if (element == L.data[i]) { j = i + 1 ; break ; } } if (j == -1 ) { printf ("Element not found.\n" ); } printf ("the first position of value:%d is %d\n" , element,j); return j; } int SerchAllPositions_by_value (SqList& L, ElemType element) { int j = -1 ; for (int i = 0 ; i <= L.length-1 ; i++) { if (element == L.data[i]) { j = i + 1 ; printf ("the position of value:%d is %d\n" , element, j); } } if (j == -1 ) { printf ("The Element of %d is not found.\n" , element); } 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 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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 #include <stdio.h> #define MaxSize 50 typedef int ElemType; typedef struct { ElemType data[MaxSize]; int length; }SqList; bool ListInsert (SqList& L, int i, ElemType element) { int i_index = i - 1 ; int length_index = L.length - 1 ; if (i_index < 0 || i_index > length_index) { printf ("the position of i is illegal , change it to less than L.length\n" ); return false ; } if (length_index == MaxSize) { printf ("MaxSize cannot insert value\n" ); return false ; } L.length++; length_index = L.length - 1 ; for (int j = length_index; j >= i_index; j--) { L.data[j] = L.data[j - 1 ]; } L.data[i_index] = element; } bool DelteElement_by_value (SqList& L, ElemType element) { int j = -1 ; for (int i = 0 ; i < L.length-1 ; i++) { if (element == L.data[i]) { j = i; break ; } } if (j == -1 ) { printf ("Element not found.\n" ); return false ; } for (int i = j; i < L.length - 1 ; i++) { L.data[i] = L.data[i + 1 ]; } L.length--; return true ; } bool DelteElement_by_position (SqList& L, int i) { int i_index = i - 1 ; int length_index = L.length - 1 ; if (i_index < 0 || i_index > length_index) { printf ("the position of i is illegal , change it to less than L.length\n" ); return false ; } for (int i = i_index; i < L.length - 1 ; i++) { L.data[i] = L.data[i + 1 ]; } L.length--; return true ; } int SerchFirstPosition_by_value (SqList& L, ElemType element) { int j = -1 ; for (int i = 0 ; i < L.length - 1 ; i++) { if (element == L.data[i]) { j = i + 1 ; break ; } } if (j == -1 ) { printf ("Element not found.\n" ); } printf ("the first position of value:%d is %d\n" , element,j); return j; } int SerchAllPositions_by_value (SqList& L, ElemType element) { int j = -1 ; for (int i = 0 ; i <= L.length-1 ; i++) { if (element == L.data[i]) { j = i + 1 ; printf ("the position of value:%d is %d\n" , element, j); } } if (j == -1 ) { printf ("The Element of %d is not found.\n" , element); } return 0 ; } void PrintList (SqList L) {for (size_t i = 0 ; i < L.length; i++){ printf ("%3d" , L.data[i]); } printf ("\n-------length=%d---------\n" , L.length);printf ("\n" );} int main () { SqList L; L.data[0 ] = 1 ; L.data[1 ] = 7 ; L.data[2 ] = 2 ; L.data[3 ] = 3 ; L.data[4 ] = 5 ; L.data[5 ] = 7 ; L.length = 6 ; PrintList (L); SerchAllPositions_by_value (L, 88 ); return 0 ; }
单链表(Single Linked List) 单链表是一种链式数据结构,用于存储一组节点。每个节点包含数据域和指针域,其中:
数据域(Data Field) :存储节点的数据。
指针域(Next Field) :存储指向下一个节点的指针。
注意 ,这里构造的单链表头节点内无值,但因visual studio2022会自动在输出时为空的值域填充随机值,故用我们这里NULL即0填充,在实际使用链表时头节点会被填充数据,写入链表长度或其他数值信息
我改主意了,头节点数据填入链表长度
在链表中,节点的概念是逻辑性的虚拟的,实际代码层面只存在包含于构造体中的指针和数据
1.创建单链表 包含数据域 和 指针域,指针用于指向下一个结构体(节点)
1 2 3 4 5 6 7 typedef int ElementType;typedef struct LNode { ElementType data; LNode* next; }LNode,*LinkList;
2.头插法添加节点
头插法是一种在链表头部插入节点的方式,适用于倒序建立链表。步骤如下:
为新节点分配内存。
设置新节点的数据值。
将新节点的 next
指针指向当前的头节点。
更新头指针,使其指向新节点。
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 List_head_insert (LinkList &L) { LNode* s; ElementType x = 0 ; L = (LinkList)malloc (sizeof (LNode)); L->data = x; L->next = nullptr ; scanf_s ("%d" , &x); while (x != 9999 ) { s = (LinkList)malloc (sizeof (LNode)); s->data = x; s->next = L->next; L->next = s; scanf_s ("%d" , &x); } } int main () { LinkList L; }
3.尾插法添加节点
尾插法是将新节点添加到链表尾部,适用于顺序建立链表。步骤如下:
为新节点分配内存。
设置新节点的数据值。
将当前尾节点的 next
指针指向新节点。
更新尾节点,使其指向新节点
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 void List_tail_insert (LinkList& L) { LNode* s,*r; ElementType x = 0 ; L = (LinkList)malloc (sizeof (LNode)); L->data = x; L->next = nullptr ; r = L; scanf_s ("%d" , &x); while (x != 9999 ) { s = (LinkList)malloc (sizeof (LNode)); s->data = x; r->next =s; r = s; scanf_s ("%d" , &x); } r->next = nullptr ; } int main () { LinkList L; }
5.单链表查询
定义计数器变量
考虑非法参数并给出返回值
用while循环从头节点开始遍历链表,由计数器控制步数
考虑查找失败结果
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 LNode* List_search_position (LinkList L, int serch_position) { int i = 0 ; if (serch_position < 1 ) { printf ("serch_position is less than 1,i will return head pointer,it means the serch_position is 1\n" ); return L; } if (serch_position > L->data) { printf ("serch_position is greater than the length of the list\n" ); return nullptr ; } while (L != nullptr && i != serch_position) { L = L->next; i++; } if (i != serch_position) { printf ("The serch_position is not found,it may because the serch_position is greater than the length of the list\n" ); return nullptr ; } return L; } LNode* List_search_value (LinkList L, ElementType x) { while (L != nullptr && L->data != x) { L = L->next; } if (L != nullptr ) { return L; } else { printf ("The value is not found\n" ); return nullptr ; } } int main () { LinkList Serch_pointer; }
6.按位置插入节点
定义一个指针变量s,用于指向新结点
考虑在第一个位置插入的结点的情况,此时直接应用头插法算法
通过List_search_position(L, insert_position - 1)
获取目标结点的前一个结点的指针域的值
判断p是否为空,决定是否执行插入算法
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 bool List_insert_position (LinkList L, int insert_position, ElementType x) { LinkList s; if (insert_position == 1 ) { s = (LinkList)malloc (sizeof (LNode)); s->data = x; s->next = L->next; L->next = s; return true ; } LinkList p = List_search_position (L, insert_position - 1 ); if (p == nullptr ) { printf ("insert_position is greater than the length of the list,you can't insert it\n" ); return false ; } else { s = (LinkList)malloc (sizeof (LNode)); s->data = x; s->next = p->next; p->next = s; } }
7.单链表删除节点
通过List_search_position(L, insert_position - 1)
获取目标结点的前一个结点的指针域的值
判断p是否为空,决定是否执行删除算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 bool List_delete_position (LinkList &L, int delete_position) { LinkList p = List_search_position (L, delete_position - 1 ); if (p == nullptr ) { printf ("delete_position is greater than the length of the list,you can't delete it\n" ); return false ; } else { LinkList q; q = p->next; p->next = q->next; free (q); return true ; } }
单链表总体代码 单链表总体代码
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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef int ElementType;typedef struct LNode { ElementType data; LNode* next; }LNode,*LinkList; void List_head_insert (LinkList &L) { LNode* s; ElementType x = 0 ; L = (LinkList)malloc (sizeof (LNode)); L->data = 99 ; L->next = nullptr ; scanf_s ("%d" , &x); while (x != 9999 ) { s = (LinkList)malloc (sizeof (LNode)); s->data = x; s->next = L->next; L->next = s; scanf_s ("%d" , &x); } } void List_tail_insert (LinkList& L) { LNode* s,*r; ElementType x = 0 ; L = (LinkList)malloc (sizeof (LNode)); L->data = 99 ; L->next = nullptr ; r = L; scanf_s ("%d" , &x); while (x != 9999 ) { s = (LinkList)malloc (sizeof (LNode)); s->data = x; r->next =s; r = s; scanf_s ("%d" , &x); } r->next = nullptr ; } LNode* List_search_position (LinkList L, int serch_position) { int i = 0 ; if (serch_position < 1 ) { printf ("serch_position is less than 1,i will return head pointer,it means the serch_position is 1\n" ); return L; } if (serch_position > L->data) { printf ("serch_position is greater than the length of the list\n" ); return nullptr ; } while (L != nullptr && i != serch_position) { L = L->next; i++; } if (i != serch_position) { printf ("The serch_position is not found,it may because the serch_position is greater than the length of the list\n" ); return nullptr ; } return L; } LNode* List_search_value (LinkList L, ElementType x) { while (L != nullptr && L->data != x) { L = L->next; } if (L != nullptr ) { return L; } else { printf ("The value is not found\n" ); return nullptr ; } } bool List_insert_position (LinkList L, int insert_position, ElementType x) { LinkList s; if (insert_position == 1 ) { s = (LinkList)malloc (sizeof (LNode)); s->data = x; s->next = L->next; L->next = s; return true ; } LinkList p = List_search_position (L, insert_position - 1 ); if (p == nullptr ) { printf ("insert_position is greater than the length of the list,you can't insert it\n" ); return false ; } else { s = (LinkList)malloc (sizeof (LNode)); s->data = x; s->next = p->next; p->next = s; } } bool List_delete_position (LinkList &L, int delete_position) { LinkList p = List_search_position (L, delete_position - 1 ); if (p == nullptr ) { printf ("delete_position is greater than the length of the list,you can't delete it\n" ); return false ; } else { LinkList q; q = p->next; p->next = q->next; free (q); return true ; } } void List_print (LinkList L) { while (L != nullptr ) { printf ("%d " , L->data); L = L->next; } printf ("\n" ); } int main () { LinkList L; LinkList Serch_pointer; List_head_insert (L); List_print (L); Serch_pointer = List_search_position (L, 1 ); printf ("The data of the serch_pointer is %d\n" , Serch_pointer->data); List_delete_position (L, 1 ); List_print (L); return 0 ; }
栈 (Stack) 栈是一种后进先出(LIFO) 的数据结构。栈中元素只能在一端添加或移除,这一端被称为“栈顶”。在栈的操作中,常用的操作有:
push :将元素压入栈顶。
pop :将栈顶元素弹出。
peek :查看栈顶元素,但不移除它。
栈的应用 :广泛用于递归算法、表达式求值、括号匹配、函数调用等场景。
栈的本质为人为控制进出规则的数组,通过控制索引来控制数组中的数据进出,栈可以顺序定义也可链式定义
顺序栈(squencial stack) 有顺序表发展而来的数据结构,其在内存中需要连续的空间
1.顺序栈结构定义 顺序栈由一个数组和一个用于标记数组索引的整型值组成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #define MAX_SIZE 100 typedef int ElementType;typedef struct Stack { ElementType data[MAX_SIZE]; int top; }SqStack; int main () { SqStack S; }
2.初始化栈 1 2 3 4 void InitStack (SqStack& S) { S.top = -1 ; }
3.入栈操作(Push)
判断是否可继续入栈
修改索引,填值
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 bool IsStackFull (SqStack S) { if (S.top == MAdata_x_SIZE - 1 ) { printf ("Stack is full!\n" ); return false ; } else return true ; } bool Push (SqStack& S, ElementType data_x) { if (IsStackFull (S)) { S.data[++S.top] = data_x; return true ; } else { return false ; } }
4.出栈操作(Pop)
判断栈内是否有值
取值,修改索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bool Pop (SqStack& S, ElementType& data_x) { if (S.top == -1 ) { printf ("Stack is empty,can not pop!\n" ); return false ; } else { data_x = S.data[S.top--]; return true ; } }
5.读取栈顶元素(Peek)
判断栈内是否有值
取值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 bool Peek (SqStack& S, ElementType& data_x) { if (S.top == -1 ) { printf ("Stack is empty,can not get top!\n" ); return false ; } else { data_x = S.data[S.top]; return true ; } }
顺序栈总体代码 顺序栈总体代码
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 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAdata_x_SIZE 8 typedef int ElementType;typedef struct Stack { ElementType data[MAdata_x_SIZE]; int top; }SqStack; void InitStack (SqStack& S) { S.top = -1 ; } bool IsStackFull (SqStack S) { if (S.top == MAdata_x_SIZE - 1 ) { printf ("Stack is full!\n" ); return false ; } else return true ; } bool Push (SqStack& S, ElementType data_x) { if (IsStackFull (S)) { S.data[++S.top] = data_x; return true ; } else { return false ; } } bool Pop (SqStack& S, ElementType& data_x) { if (S.top == -1 ) { printf ("Stack is empty,can not pop!\n" ); return false ; } else { data_x = S.data[S.top--]; return true ; } } bool GetTop (SqStack& S, ElementType& data_x) { if (S.top == -1 ) { printf ("Stack is empty,can not get top!\n" ); return false ; } else { data_x = S.data[S.top]; return true ; } } void PrintStack (SqStack S) { if (S.top == -1 ) { printf ("Stack is empty,nothing to print!\n" ); return ; } for (int i = 0 ; i <= S.top; i++) { printf ("%d " , S.data[i]); } printf ("\n" ); } int main () { SqStack S; InitStack (S); Push (S, 1 ); Push (S, 2 ); Push (S, 3 ); Push (S, 4 ); Push (S, 5 ); Push (S, 6 ); Push (S, 7 ); Push (S, 8 ); Push (S, 9 ); Push (S, 10 ); for (int i = 0 ; i < MAdata_x_SIZE; i++) { if (S.top == -1 ) break ; PrintStack (S); ElementType y; GetTop (S,y); printf ("Top element is %d\n" , y); Pop (S, y); printf ("Pop element is %d\n" , y); printf ("After pop, stack is:\n" ); PrintStack (S); printf ("\n" ); } return 0 ; }
链栈(Link Stack) 由链表发展而来,使用零散内存空间
1. 链栈定义 完全继承链表定义形式
1 2 3 4 5 6 7 8 9 10 11 #define ElemType int typedef struct LinkStack { ElemType data; struct LinkStack * next; }*LS; int main () { LS L; }
2. 链栈初始化 由于链表仅能由头结点开始遍历的特性,考虑到后续操作,链栈最佳初始化方案为头插法
1 2 3 4 5 6 7 8 9 void createStack (LS &L) { LS p; ElemType x = 0 ; L = (LS)malloc (sizeof (struct LinkStack)); L->data = x; L->next = nullptr ; }
3.栈的状态 链栈没必要设置上限,除非是大数据流进入,否则零散利用内存的分配方式几乎不可能将内存占满
1 2 3 4 5 6 7 bool isEmpty (LS L) { if (L->next == nullptr ) return true ; else return false ; }
4. 入栈 算法与头插法创建链表相同
1 2 3 4 5 6 7 8 void push (LS& L, ElemType x) { LS p; p = (LS)malloc (sizeof (struct LinkStack)); p->data = x; p->next = L->next; L->next = p; }
5.出栈 调用并删除链表中第一个结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 bool pop (LS& L, ElemType& x) { LS p; if (L->next == nullptr ) { printf ("Stack is empty!\n" ); return false ; } p = L->next; x = p->data; L->next = p->next; free (p); return true ; }
6. 获取栈顶数据 1 2 3 4 5 6 7 8 9 10 bool getTop (LS L, ElemType& x) { if (L->next == nullptr ) { printf ("Stack is empty!\n" ); return false ; } x = L->next->data; return true ; }
链栈总体代码 链栈总体代码
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> #include <string.h> #define ElemType int typedef struct LinkStack { ElemType data; struct LinkStack * next; }*LS; void createStack (LS &L) { LS p; ElemType x = 0 ; L = (LS)malloc (sizeof (struct LinkStack)); L->data = x; L->next = nullptr ; } bool isEmpty (LS L) { if (L->next == nullptr ) return true ; else return false ; } void push (LS& L, ElemType x) { LS p; p = (LS)malloc (sizeof (struct LinkStack)); p->data = x; p->next = L->next; L->next = p; } bool pop (LS& L, ElemType& x) { LS p; if (L->next == nullptr ) { printf ("Stack is empty!\n" ); return false ; } p = L->next; x = p->data; L->next = p->next; free (p); return true ; } bool getTop (LS L, ElemType& x) { if (L->next == nullptr ) { printf ("Stack is empty!\n" ); return false ; } x = L->next->data; return true ; } void printStack (LS L) { while (L->next!= nullptr ) { printf ("%d " , L->next->data); L = L->next; } printf ("\n" ); } int main () { LS L; createStack (L); push (L, 1 ); push (L, 2 ); push (L, 3 ); push (L, 4 ); printStack (L); ElemType x; pop (L, x); printf ("poped element is %d\n" , x); printStack (L); getTop (L, x); printf ("top element is %d\n" , x); printStack (L); return 0 ; }
队列(Queue) 队列是一种先进先出(FIFO) 的数据结构。队列中元素只能从一端进入(队尾),从另一端移除(队头)。常用操作包括:
enqueue :将元素加入队尾。
dequeue :将队头元素移除。
peek :查看队头元素。
队列的应用 :广泛用于任务调度、广度优先搜索、数据缓冲等场景。
循环(顺序)队列(SquenceQueue) 1. 创建队列 循环队列又一个自定义的数组和队首与队尾两个逻辑指针构成
1 2 3 4 5 6 7 8 9 #define MAX_SIZE 5 #define ElementType int typedef struct { ElementType data[MAX_SIZE]; int front, rear; } SquenceQueue;
2. 队列初始化 规定队列首尾标相等时为空
1 2 3 4 5 6 7 8 void InitQueue (SquenceQueue& SQ) { SQ.front = SQ.rear = 0 ; } int main () { SquenceQueue SQ; }
3. 入队 循环队列核心算法 :
(SQ.rear + 1) % MAX_SIZE
算法实现了索引循环的功能,且SQ.rear + 1
避免了在数组被占满时尾标与头标重合的情况,即组下标越界,使得队列满时队首和队尾重合,再插入元素时会覆盖队首元素
该算法规定队首和队尾相等时为空,(SQ.rear + 1) % MAX_SIZE
的存在SQ.rear
和SQ.front
的取值范围为[0,MAX_SIZE-1]
,构造体中的数组最后一位的被索引跳过,该存储单元值为0,且不会被调用
1 2 3 4 5 6 7 8 9 10 11 bool EnQueue (SquenceQueue& SQ,ElementType value) { if ((SQ.rear + 1 ) % MAX_SIZE == SQ.front) { printf ("Queue is full!\n" ); return false ; } SQ.data[SQ.rear] = value; SQ.rear = (SQ.rear + 1 ) % MAX_SIZE; return true ; }
4. 出队 与入队原理相同
1 2 3 4 5 6 7 8 9 10 11 bool DeQueue (SquenceQueue& SQ, ElementType& value) { if (SQ.front == SQ.rear) { printf ("Queue is empty!\n" ); return false ; } value = SQ.data[SQ.front]; SQ.front = (SQ.front + 1 ) % MAX_SIZE; return true ; }
5. 查看队首元素 1 2 3 4 5 6 7 8 9 10 11 bool peek (SquenceQueue SQ, ElementType& value) { if (SQ.front == SQ.rear) { printf ("Queue is empty!\n" ); return false ; } return false ; value = SQ.data[SQ.front]; return true ; }
循环队列总体代码 循环队列总体代码
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> #include <string.h> #define MAX_SIZE 5 #define ElementType int typedef struct { ElementType data[MAX_SIZE]; int front, rear; } SquenceQueue; void InitQueue (SquenceQueue& SQ) { SQ.front = SQ.rear = 0 ; } bool EnQueue (SquenceQueue& SQ,ElementType value) { if ((SQ.rear + 1 ) % MAX_SIZE == SQ.front) { printf ("Queue is full!\n" ); return false ; } SQ.data[SQ.rear] = value; SQ.rear = (SQ.rear + 1 ) % MAX_SIZE; return true ; } bool DeQueue (SquenceQueue& SQ, ElementType& value) { if (SQ.front == SQ.rear) { printf ("Queue is empty!\n" ); return false ; } value = SQ.data[SQ.front]; SQ.front = (SQ.front + 1 ) % MAX_SIZE; return true ; } void PrintQueue (SquenceQueue SQ) { if (SQ.front == SQ.rear) { printf ("Queue is empty!\n" ); return ; } for (int i = SQ.front; i != SQ.rear; i = (i + 1 ) % MAX_SIZE) { printf ("%d " , SQ.data[i]); } printf ("\n" ); } bool peek (SquenceQueue SQ, ElementType& value) { if (SQ.front == SQ.rear) { printf ("Queue is empty!\n" ); return false ; } return false ; value = SQ.data[SQ.front]; return true ; } int main_SQ () { SquenceQueue SQ; InitQueue (SQ); EnQueue (SQ, 11 ); EnQueue (SQ, 22 ); EnQueue (SQ, 33 ); EnQueue (SQ, 44 ); EnQueue (SQ, 55 ); PrintQueue (SQ); ElementType testvalue; DeQueue (SQ, testvalue); printf ("DeQueue value is %d\n" , testvalue); PrintQueue (SQ); DeQueue (SQ, testvalue); printf ("DeQueue value is %d\n" , testvalue); PrintQueue (SQ); EnQueue (SQ, 66 ); PrintQueue (SQ); return 0 ; }
链队列(LinkQueue) 链队列由一个单链表和分别指代首尾的两个指针构成,用两个构造体分别构造单链表和首尾指针组
1. 构建链队列 1 2 3 4 5 6 7 8 9 10 11 12 13 #define ElementType int typedef struct LinkQueueNode { ElementType data; LinkQueueNode *next; } LQN; typedef struct LinkQueuePointers { LQN *front; LQN *rear; } LQP;
1. 初始化链队列 由于构造体LinkQueuePointers
包含了用于指向LinkQueueNode
的指针,因此只需创LinkQueuePointers
的实例即可通过内置的指针来向内存申请空间
注意 : (LQN*)malloc(sizeof(struct LinkQueuePointers));
这行代码的含义是向内存空间申请一个大小为两个指针空间,且这连个指针式用于指向构造体LinkQueuePointers
的
1 2 3 4 5 6 7 8 9 10 11 void InitQueue (LQP &P) { P.front = P.rear = (LQN*)malloc (sizeof (struct LinkQueuePointers)); P.front->next = NULL ; } int main () { LQP P; }
1. 入队 单链表尾插法入队,链表在内存空间足够大的情况下无需考虑长度上限问题
1 2 3 4 5 6 7 8 void EnQueue (LQP& P, ElementType x) { LQN *newp = (LQN*)malloc (sizeof (struct LinkQueuePointers)); newp->data = x; P.rear->next = newp; P.rear = newp; newp->next = nullptr ; }
1. 出队
判断队列是否为空
拿到第一个结点的数据并断链和释放内存
考虑当取出最后一个结点元素时,尾指针与头指针复位的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 bool DeQueue (LQP& P, ElementType& x) { if (P.front == P.rear) { printf ("Queue is Empty,nothing can DeQueue" ); return false ; } LQN* dep = P.front->next; x = dep->data; P.front->next = dep->next; if (P.rear == dep) { P.rear = P.front; } free (dep); return true ; }
1. 查看队首元素 1 2 3 4 5 6 7 8 9 10 11 12 bool PeekQueue (LQP P, ElementType &x) { if (P.front == P.rear) { printf ("Queue is Empty,nothing can DeQueue" ); return false ; } LQN* peekp = P.front->next; x = peekp->data; return true ; }
链式队列总体代码 链式队列总体代码
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 #include <stdio.h> #include <stdlib.h> #include <string.h> #define ElementType int typedef struct LinkQueueNode { ElementType data; LinkQueueNode *next; } LQN; typedef struct LinkQueuePointers { LQN *front; LQN *rear; } LQP; void InitQueue (LQP &P) { P.front = P.rear = (LQN*)malloc (sizeof (struct LinkQueuePointers)); P.front->next = nullptr ; } void EnQueue (LQP& P, ElementType x) { LQN *newp = (LQN*)malloc (sizeof (struct LinkQueuePointers)); newp->data = x; P.rear->next = newp; P.rear = newp; newp->next = nullptr ; } bool DeQueue (LQP& P, ElementType& x) { if (P.front == P.rear) { printf ("Queue is Empty,nothing can DeQueue" ); return false ; } LQN* dep = P.front->next; x = dep->data; P.front->next = dep->next; if (P.rear == dep) { P.rear = P.front; } free (dep); return true ; } bool PeekQueue (LQP P, ElementType &x) { if (P.front == P.rear) { printf ("Queue is Empty,nothing can DeQueue" ); return false ; } LQN* peekp = P.front->next; x = peekp->data; return true ; } void PrintQueue (LQP P) { LQN* p = P.front->next; while (p!= NULL ) { printf ("%d " , p->data); p = p->next; } printf ("\n" ); } int main () { LQP P; InitQueue (P); EnQueue (P, 1 ); EnQueue (P, 2 ); EnQueue (P, 3 ); EnQueue (P, 4 ); EnQueue (P, 5 ); PrintQueue (P); ElementType x = 0 ; DeQueue (P, x); printf ("dequeue is %d\n" , x); PrintQueue (P); DeQueue (P, x); printf ("dequeue is %d\n" , x); PrintQueue (P); EnQueue (P, 6 ); PrintQueue (P); PeekQueue (P, x); printf ("front of queue is %d\n" , x); return 0 ; }
树 二叉树(BinaryTree) 二叉树 是一种树形数据结构,其中每个节点最多有两个子节点,称为左子节点和右子节点。二叉树具有层次关系,广泛用于数据组织和处理。
定义二叉树 二叉树主体由两个指针和一个数据组成,但构建二叉树还需要队列的辅助,采用链式队列为优
1 2 3 4 5 6 7 8 9 10 11 12 13 14 typedef char BT_ElemType;typedef struct BiTreeNode { BT_ElemType data; struct BiTreeNode *lchild, *rchild; } BiTreeNode, *BiTree; typedef struct BiTreeTagList { BiTree p; struct BiTreeTagList *p_next; }*BiTreeTag;
层次创建二叉树 依照输入字符的顺序依次从上到下从左到右插入二叉树中
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 void BiTreeCreate_by_InputChar (BiTree &root) { root = NULL ; BiTree pnew; char c; BiTreeTag phead = NULL , ptail = NULL ; BiTreeTag listpnew = NULL ; BiTreeTag pcur = NULL ; while (scanf_s ("%c" , &c)) { if (c == '\n' ) { break ; } pnew = (BiTree)calloc (1 , sizeof (BiTreeNode)); pnew->data = c; listpnew = (BiTreeTag)calloc (1 , sizeof (BiTreeTag)); listpnew->p = pnew; if (NULL == root) { root = pnew; phead = listpnew; ptail = listpnew; pcur = listpnew; } else { ptail->p_next = listpnew; ptail = listpnew; if (pcur->p->lchild == NULL ) { pcur->p->lchild = pnew; } else if (pcur->p->rchild == NULL ) { pcur->p->rchild = pnew; pcur = pcur->p_next; } } } }
先序遍历(深度优先遍历) 1 2 3 4 5 6 7 8 void PreOrder (BiTree root) { if (root == NULL ) return ; printf ("%c " , root->data); PreOrder (root->lchild); PreOrder (root->rchild); }
中序遍历 1 2 3 4 5 6 7 8 void InOrder (BiTree root) { if (root == NULL ) return ; InOrder (root->lchild); printf ("%c " , root->data); InOrder (root->rchild); }
后序遍历 1 2 3 4 5 6 7 8 void PostOrder (BiTree root) { if (root == NULL ) return ; PostOrder (root->lchild); PostOrder (root->rchild); printf ("%c " , root->data); }
层序遍历 需要辅助队列进行打印操作,这里使用的头文件来引用ListQueue的数据结构和方法
节点入队
打印并出队
判断出队的节点左右孩子是否为空,不为空则使其左右孩子入队,为空则继续
返回 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void LevelOrder (BiTree root) { LQP Q; BiTree p; InitQueue (Q); EnQueue (Q,root); while (!IsEmpty (Q)) { DeQueue (Q, p); putchar (p->data); if (p->lchild != NULL ) { EnQueue (Q, p->lchild); } if (p->rchild != NULL ) { EnQueue (Q, p->rchild); } } }
头文件和队列方法
头文件
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 #pragma once #include <stdio.h> #include <stdlib.h> #include <string.h> typedef char BT_ElemType;typedef struct BiTreeNode { BT_ElemType data; struct BiTreeNode * lchild, * rchild; } BiTreeNode, *BiTree; typedef struct BiTreeTagList { BiTree p; struct BiTreeTagList * p_next; }*BiTreeTag; typedef BiTree ElementType;typedef struct LinkQueueNode { ElementType data; LinkQueueNode* next; } LQN; typedef struct LinkQueuePointers { LQN* front; LQN* rear; } LQP; void InitQueue (LQP& P) ;void EnQueue (LQP& P, ElementType x) ;bool IsEmpty (LQP P) ;bool DeQueue (LQP& P, ElementType& x) ;
队列方法
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 void InitQueue (LQP &P) { P.front = P.rear = (LQN*)malloc (sizeof (struct LinkQueuePointers)); P.front->next = nullptr ; } void EnQueue (LQP& P, ElementType x) { LQN *newp = (LQN*)malloc (sizeof (struct LinkQueuePointers)); newp->data = x; P.rear->next = newp; P.rear = newp; newp->next = nullptr ; } bool IsEmpty (LQP P) { return P.front == P.rear; } bool DeQueue (LQP& P, ElementType& x) { if (P.front == P.rear) { printf ("Queue is Empty,nothing can DeQueue" ); return false ; } LQN* dep = P.front->next; x = dep->data; P.front->next = dep->next; if (P.rear == dep) { P.rear = P.front; } free (dep); return true ; }
二叉树创建与遍历总体代码 二叉树创建与遍历总体代码
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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 #include "C_L_Header.h" typedef char BT_ElemType;typedef struct BiTreeNode { BT_ElemType data; struct BiTreeNode * lchild, * rchild; } BiTreeNode, *BiTree; typedef struct BiTreeTagList { BiTree p; struct BiTreeTagList * p_next; }*BiTreeTag; void BiTreeCreate_by_InputChar (BiTree &root) { root = NULL ; BiTree pnew; char c; BiTreeTag phead = NULL , ptail = NULL ; BiTreeTag listpnew = NULL ; BiTreeTag pcur = NULL ; while (scanf_s ("%c" , &c)) { if (c == '\n' ) { break ; } pnew = (BiTree)calloc (1 , sizeof (BiTreeNode)); pnew->data = c; listpnew = (BiTreeTag)calloc (1 , sizeof (BiTreeTag)); listpnew->p = pnew; if (NULL == root) { root = pnew; phead = listpnew; ptail = listpnew; pcur = listpnew; } else { ptail->p_next = listpnew; ptail = listpnew; if (pcur->p->lchild == NULL ) { pcur->p->lchild = pnew; } else if (pcur->p->rchild == NULL ) { pcur->p->rchild = pnew; pcur = pcur->p_next; } } } } void PreOrder (BiTree root) { if (root == NULL ) return ; printf ("%c " , root->data); PreOrder (root->lchild); PreOrder (root->rchild); } void InOrder (BiTree root) { if (root == NULL ) return ; InOrder (root->lchild); printf ("%c " , root->data); InOrder (root->rchild); } void PostOrder (BiTree root) { if (root == NULL ) return ; PostOrder (root->lchild); PostOrder (root->rchild); printf ("%c " , root->data); } void PrintTree (BiTree root, const char * prefix, int isLeft) { if (root == NULL ) { return ; } printf ("%s" , prefix); printf (isLeft ? "├── " : "└── " ); printf ("%c\n" , root->data); char newPrefix[100 ]; snprintf (newPrefix, sizeof (newPrefix), "%s%s" , prefix, isLeft ? "│ " : " " ); if (root->lchild || root->rchild) { PrintTree (root->rchild, newPrefix, 1 ); PrintTree (root->lchild, newPrefix, 0 ); } } void LevelOrder (BiTree root) { LQP Q; BiTree p; InitQueue (Q); EnQueue (Q,root); while (!IsEmpty (Q)) { DeQueue (Q, p); putchar (p->data); if (p->lchild != NULL ) { EnQueue (Q, p->lchild); } if (p->rchild != NULL ) { EnQueue (Q, p->rchild); } } } int main () { BiTree Btree01; printf ("请输入二叉树节点序列(以换行结束):\n" ); BiTreeCreate_by_InputChar (Btree01); printf ("先序遍历结果:\n" ); PreOrder (Btree01); printf ("\n" ); printf ("中序遍历结果:\n" ); InOrder (Btree01); printf ("\n" ); printf ("后序遍历结果:\n" ); PostOrder (Btree01); printf ("\n" ); printf ("层序遍历结果:\n" ); LevelOrder (Btree01); printf ("\n" ); printf ("树状打印结果:\n" ); PrintTree (Btree01, "" , 0 ); return 0 ; }
哈夫曼树(Huffman Tree) 以后再来写
哈夫曼编码