1 //当只需要做查找操作的时候 最好的是用顺序表O(1)
2 //经常需要做插入和删除的时候 用链表,当然如果这个位置在表末尾,用顺序表也行
3 //当经常删除和插入的位置在表头或者中间的时候,顺序表要挪很多次
4 //空间上:链表要比顺序表消耗大 因为多了指针域
5 /*C++中用stl list就能用链表了 头文件是<list>
6 JAVA中用链表就是引入LinkedList 和ArrayList的使用方法一样
7 */
8
9 /*
10 带头指针:第一个头结点就存放数据,但是有head pointer指向这个节点
11 带头结点:第一个头结点不存放数据,只是用来索引链表的起始位置
12 相比较:
13 head node
14 |
15 [ ] -> [ ] -> [ ] -> ....
16
17 带头结点的比带头指针的要多耗一个空间
18
19
20 head pointer
21 |
22 ?????? -> [ ] -> [ ] -> [ ]
23 带头指针在遍历过程中会(可以)发生变化
24 而带头结点的不能改变指针域,只能改变头结点的指针域
25 */
26 #include<cstdio>
27 #include<iostream>
28 #include<cstdlib>
29
30 using namespace std;
31
32 struct Node{
33 int data;
34 struct Node* next;
35 };
36
37 typedef struct Node* LList;
38
39 void init(struct Node **phead){
40 *phead = NULL;
41 }
42
43 int getLength(struct Node* head){
44 int len = 0;
45 while(head != NULL){
46 len ++;
47 head = head->next;//并没有改变头指针 只是修改了head这个局部变量
48 }
49
50 return len;
51 }
52
53 void printList(struct Node* head){
54 while(head!=NULL){
55 printf("%d, ",head->data);
56 head = head->next;//并没有改变头指针 只是修改了head这个局部变量
57 }
58 printf("\n");
59 }
60
61 struct Node* createNode(int x){
62 struct Node* t;
63 //这里应该要加入一个判断剩余空间是否足够malloc
64 t = (struct Node*)malloc(sizeof(struct Node));
65 t->next = NULL;
66 t->data = x;
67 return t;
68 }
69
70 //因为查找第k-1个点在insert和remove中都要用到
71 //我们在编程中应该尽量不要去复制代码 而是封装成函数
72 //尽管函数名叫findK,但是我们找的是第k-1个,所以函数传值传的是k-1
73 struct Node * findKth(struct Node* head, int k){
74 int count = 1;
75 struct Node* p;
76 p = head;
77 while(p != NULL && count < k){
78 p = p->next;
79 count++;
80 }
81 //p可能为空 但是无所谓 下面调用这个函数的地方也有判断语句
82 return p;
83 }
84
85
86 //** 指针的指针,存的是指针的地址
87 bool insert(struct Node** phead, int k, int x){
88 if(k < 1){
89 return false;
90 }
91 //在第一个点插入,要把头指针指向这个点
92 else if(k == 1){
93 struct Node *t;
94 //注意createNode后,t->next = NULL
95 //假如这个链表原来不是个空表的话,原来的数据都会丢失
96 t = createNode(x);
97 //要把原先的点的next赋值给t才行
98 t->next = *phead;
99 *phead = t;
100 return true;
101 }
102
103 //在第一个点后面插入的情况
104 //一个个去遍历找到k-1的位置 如果k-1不存在那就没法插入了
105 //例如1 2 3 4,插在6是插不了,插在5就可以
106 struct Node * p;//存k-1的位置
107 /*
108 int count = 1;//用来计数
109 p = *phead;
110 //当循环判断条件里面的范围不确定的时候,先试着运行一下等下再修改也行
111 while(p!=NULL && count < k - 1){
112 p = p->next;
113 count ++;
114 }
115 封装成了findKth
116 */
117 //尽管函数名叫findK,但是我们找的是第k-1个,所以函数传值传的是k-1
118 p = findKth(*phead, k - 1);
119
120 if(p){
121 struct Node* t;
122 //因为(struct Node*)malloc(sizeof(struct Node));要在很多地方用
123 //干脆就搞成一个函数算了
124 t = createNode(x);
125 t->next = p->next;
126 p->next = t;
127 return true;
128 }
129 else{// 1 2 3 4你要插入6,p会指向前一个,就是空的,就插不进去
130 return false;
131 }
132 }
133
134 bool removeNode(struct Node** phead, int k, int *px){
135 //区分第一个节点和后面的节点
136 if(k < 0){
137 return false;
138 }
139 if(k == 1){
140 if(*phead != NULL){
141 *px = (*phead)->data;
142 *phead = (*phead)->next;
143 return true;
144 }
145 //*phead为空的时候这个表是个空表
146 else return false;
147
148 }
149 else{
150 struct Node *p;
151 p = findKth(*phead, k - 1);
152 if(p == NULL || p->next == NULL){
153 //要删除第k个,当没有第k-1位置,或者k-1往下是空(就没有第k个,也是错)
154 return false;
155 }
156 struct Node* t;
157 t = p->next;
158 p->next = t->next;
159 *px = t->data;
160 free(t);
161 return true;
162 }
163 }
164
165 int main(){
166 LList head;//头指针
167 //初始化 创建空链表
168 init(&head);
169 //遍历(打印 求表长)
170 int k = getLength(head);
171 //printf("%d\n",k); 测试
172 printList(head);
173 //int falg = insert(&head, 1, 11); 测试插入
174 insert(&head, 1, 11);//head表示哪个表,因为头指针可能会变,所以传head的地址;位置;元素数据
175 insert(&head, 1, 22);
176 insert(&head, 2, 33);
177 insert(&head, 4, 44);
178 insert(&head, 6, 55);
179 printList(head);
180 int x;//传入x的地址,用x来获取删除的点的数值
181 removeNode(&head, 1, &x);//删除第一个点的时候会改变头指针,所以传入地址
182 //printf("%d\n",x);
183 printList(head);
184 return 0;
185 }