目录
1、二阶指针做为形参的目的(使实参的值发生改变)
2、链表头插入法工作流程及测试
3、链表尾插入法工作流程及测试
3.1调试deleteElementByIndex()函数发现,主函数中的linkPtr经过showlinkList()函数之后已经成为了NULL空指针
3.2改进
4、优先队列(堆)测试
4.1++i和i++运算符的测试
1、二阶指针作为形参的目的
1)普通变量做为形参,不能改变主函数中实参的值
例子:
1 //01)形参改变,实参并没有改变的例子 2 #include3 4 using std::cout; 5 using std::endl; 6 7 /*交换a和b的值子函数*/ 8 void changeParameters(int a, int b) 9 { 10 int temp; 11 temp = a; 12 a = b; 13 b = temp; 14 } 15 16 int main() 17 { 18 int num1 = 12; 19 int num2 = 22; 20 cout << num1 << " , " << num2 << endl; 21 changeParameters(num1, num2); //此时num1和num2知识形参交换,实际参数并没有交换 22 cout << num1 << " , " << num2 << endl; 23 24 system("pause"); 25 return 0; 26 }
运行结果:
2)一阶指针作为形参,变量地址作为实参可以改变主函数中实参的值
例子:
1 //02)实参发生改变的例子 2 #include3 4 using std::cout; 5 using std::endl; 6 7 /*交换a和b的值子函数*/ 8 void changeParameters(int* a, int* b) 9 { 10 int temp; //此处改成int* temp交换地址,然后把*a和*b去掉也是可以的 11 temp = *a; 12 *a = *b; 13 *b = temp; 14 } 15 16 int main() 17 { 18 int num1 = 12; 19 int num2 = 22; 20 cout << num1 << " , " << num2 << endl; 21 changeParameters(&num1, &num2); //此时是对存储空间这的两个数进行交换,所以num1和num2的值会发生交换 22 cout << num1 << " , " << num2 << endl; 23 24 system("pause"); 25 return 0; 26 }
运行结果:
3)二阶指针做为形参,对指针用取地址运算符取地址后作为实参,可以改变实参的值
例子:
1 #include2 3 using std::cout; 4 using std::endl; 5 6 /*交换a和b的值子函数(相关解释见下)*/ 7 void changeParameters(int** a, int** b) 8 { 9 int* temp; 10 temp = *a; //保存*a指向的地址 11 *a = *b; //将*b指向的地址赋给*a指向的地址 12 *b = temp; 13 } 14 15 int main() 16 { 17 int a = 12; 18 int b = 22; 19 int* num1 = &a; 20 int* num2 = &b; 21 cout << *num1 << " , " << *num2 << endl; 22 changeParameters(&num1, &num2); //对指针取地址,即只想指针的指针,或者是地址的地址 23 cout << *num1 << " , " << *num2 << endl; 24 25 system("pause"); 26 return 0; 27 } 28 29 /* 30 int a = 12; //假设存储a变量的地址是00424A30 31 int* pc = &a; //则pc指向地址00424A30,假设存储pc的地址是00424A38 32 int** ppc = &pc; //ppc指向pc的地址(00424A38) 33 34 cout << "a的地址是: " << &a << endl; //打印 a的地址是: 00424A30 35 cout << "pc的值是: " << pc << endl; //打印 pc的值是: 00424A30 pc的值就是a的地址 36 cout << "*pc的值是: " << pc << endl; //打印 *pc的值是: 12 37 cout << "pc的地址是: " << &pc << endl; //打印 *pc的值是: 00424A38 38 cout << "ppc的值是: " << ppc << endl; //打印 ppc的值是: 00424A38 (ppc的值就是pc的地址) 39 cout << "*ppc的值是: " << ppc << endl; //打印 ppc的值是: 00424A38 (ppc的值就是pc的地址) 40 cout << "*(*ppc)的值是: " << *(*ppc) << endl; //打印 ppc的值是: 2 41 */
运行结果:
在例子中的一个解析:
1 int a = 12; //假设存储a变量的地址是00424A30 2 int* pc = &a; //则pc指向地址00424A30,假设存储pc的地址是00424A38 3 int** ppc = &pc; //ppc指向pc的地址(00424A38) 4 5 cout << "a的地址是: " << &a << endl; //打印 a的地址是: 00424A30 6 cout << "pc的值是: " << pc << endl; //打印 pc的值是: 00424A30 pc的值就是a的地址 7 cout << "*pc的值是: " << pc << endl; //打印 *pc的值是: 12 8 9 cout << "pc的地址是: " << &pc << endl; //打印 *pc的值是: 00424A38 10 cout << "ppc的值是: " << ppc << endl; //打印 ppc的值是: 00424A38 (ppc的值就是pc的地址) 11 12 cout << "*ppc的值是: " << ppc << endl; //打印 ppc的值是: 00424A38 (ppc的值就是pc的地址) 13 cout << "*(*ppc)的值是: " << *(*ppc) << endl; //打印 ppc的值是: 2
参考博客:https://blog.csdn.net/qq_34991245/article/details/81868212
04)二阶指针作为形参,一阶指针的地址作为实参,将形参赋给子函数中的一个变量,后改变该变量的值不会影响实参的值
例子:
1 #include2 3 using std::cout; 4 using std::endl; 5 6 /*交换a和b的值子函数(相关解释见下)*/ 7 void changeParameters(int** a, int** b) 8 { 9 int* temp = *a; 10 int p = 33; 11 temp = &p; //验证一下实参a的值会不会改变,此句不会改变实参a的值 12 *temp = p; //此句不会改变实参a的值 13 } 14 15 int main() 16 { 17 int a = 12; 18 int b = 22; 19 int* num1 = &a; 20 int* num2 = &b; 21 cout << *num1 << " , " << *num2 << endl; 22 changeParameters(&num1, &num2); //对指针取地址,即只想指针的指针,或者是地址的地址 23 cout << *num1 << " , " << *num2 << endl; 24 25 system("pause"); 26 return 0; 27 }
运行结果:
05)二阶指针作为形参,一阶指针的地址作为实参,改变子涵数(形参)实参的值会影响实参的值
1 #include2 3 using std::cout; 4 using std::endl; 5 6 /*交换a和b的值子函数(相关解释见下)*/ 7 void changeParameters(int** a, int** b) 8 { 9 int p = 33; 10 *a = &p; //直接对形参(实参)赋值这样是会改变实参的值的 11 } 12 13 int main() 14 { 15 int a = 12; 16 int b = 22; 17 int* num1 = &a; 18 int* num2 = &b; 19 cout << *num1 << " , " << *num2 << endl; 20 changeParameters(&num1, &num2); //对指针取地址,即只想指针的指针,或者是地址的地址 21 cout << *num1 << " , " << *num2 << endl; 22 23 system("pause"); 24 return 0; 25 }
运行结果:
2、链表头插入法工作流程及测试
01)头插法工作流程:
1 /*链表*/ 2 3 #include4 5 using std::cin; 6 using std::cout; 7 using std::endl; 8 9 typedef int ElementType; 10 11 /*定义一个结构*/ 12 struct linkList 13 { 14 ElementType Element; //定义结构中的一个数据(数据域) 15 linkList* next; //定义指向下一个结构的指针(指针域) 16 }; 17 18 typedef struct linkList *PtrtoNode; //PtrtoNode是一个类型,可以定义变量,且PtrtoNode是一个指针,指向结构体Node 19 typedef PtrtoNode List; //为PtrtoNode起别名为List 20 typedef PtrtoNode Position; //为PtrtoNode起别名为Position 21 22 void initlinkList(linkList** head); 23 void destroylinkListByHead(linkList** head); 24 void destroylinkListByTail(linkList** head); 25 void insertDatabyHead(linkList** head); 26 void insertDatabyTail(linkList* head); 27 void showlinkList(linkList** head); 28 linkList* indexof(linkList* head, int index); 29 int deleteElementByIndex(linkList* head, int index);
1 #include "stacktp1.h" 2 3 /* 4 01)链表的初始化(给头节点申请空间),所以在使用改函数的时候,只传入已创建结构的next指针即可; 5 02)使用指向指针的指针作为形参的原因在于要传入的参数是一个指针(要传入的参数是结构中的next指针), 6 如果形参定义为一层指针,那么就会发生形参改变而实参并没有发生任何改变的情况。 7 */ 8 void initlinkList(linkList** head) 9 { 10 if ((*head) == NULL) //如果结构这的next指针为空,那么表明已经初始化完成;刚刚这里写成了 != 导致在插入数据的时候head->next访问不了 11 return; 12 *head = (linkList*)malloc(sizeof(linkList)); //给结构分配足够的内存空间 13 (*head)->Element = 0; 14 (*head)->next = NULL; //head此时即是头节点也是尾节点,因为此时head指向了NULL 15 } 16 17 /* 18 01)链表的销毁 19 02)链表所有节点包括头结点都是动态申请的堆空间,使用完毕后必须手动释放,这里的销毁要把所有的节点空间全部释放; 20 03)方法一:从链表头开始遍历,也就是从前向后逐个释放每一个节点的空间 21 */ 22 void destroylinkListByHead(linkList** head) 23 { 24 linkList* header = *head; 25 linkList* p; 26 while ( header != NULL) //原来这里的条件是(*head)!=NULL,导致p->next指向了空指针;或者是循环了无数次 27 { 28 p = header; //讲当前数据节点保存下来 29 header = p->next; //将下一个数据节点保存下来 30 free(p); //讲当前节点空间释放 31 } 32 free(header); //释放头节点 33 head = NULL; //空间释放完的指针指向空指针 34 } 35 36 /* 37 01)方法二:从链表尾部向头部开始销毁,就不用临时保存 38 02)用递归遍历到最后一个结点,逐层向上返回,销毁每一个节点,顺序就是从头尾向头结点的顺序销毁。 39 */ 40 void destroylinkListByTail(linkList** head) 41 { 42 if ((*head) == NULL) 43 return; 44 destroylinkListByTail(&((*head)->next)); //递归调用 45 free(*head); 46 head = NULL; 47 } 48 49 /* 50 01)向链表这插入数据 51 02)头插法:每次在头结点H的后面插入一个输入的数据,链表中的数据顺序和实际输入顺序相反 52 03)插入的过程主要是:先申请一个新的结点,链表不像数组一次性分配指定长度的空间, 53 链表是需要增长一个就再申请一份,然后链接起来。申请完了之后给节点赋值,让新申请的节 54 点指向头结点的next,也就是node->next = h->next,再让头结点指向这个新节点, 55 H->next = node就完成插入操作。 56 04)传入的参数可以是头结点(头节点指向的是NULL) 57 05)传入的参数也可以是一个linkList结构的地址 58 06)以为此时是要向一个链表中插入数据,所以在使用malloc之后并没有释放内存 59 */ 60 void insertDatabyHead(linkList** head) 61 { 62 linkList* node; //新建一个需要插入的节点 63 int x; 64 cout << "请输入数据(-1结束):" << endl; 65 cin >> x; 66 while (x != -1) 67 { 68 node = (linkList*) malloc(sizeof(linkList)); //为需要插入的节点分配空间 69 node->Element = x; 70 //如果头节点指向的是NULL,所以下面这一句node->next换成NULL也可以 71 node->next = (*head)->next; //使node成为head指向的一个节点之后的节点 72 (*head)->next = node; //使 73 cout << "请输入数据(-1结束):" << endl; //接着输入数据 74 cin >> x; 75 } 76 } 77 78 /* 79 01)向链表这插入数据 80 02)尾插法:每次插入新的数据在链表的尾部插入就行,链表中的数据顺序和实际输入顺序相同 81 03)先找到链表的尾节点H->next == NULL,就是最后一个节点,同样插入就行。相比头插法, 82 尾插法插入数据的时候如果链表不是一条空链表,得遍历先找到尾节点。 83 04)传入的参数的头结点(头节点指向的是NULL) 84 05)输入的参数是一个linkList结构的地址 85 */ 86 87 void insertDatabyTail(linkList* head) 88 { 89 linkList* node; 90 linkList* remove; 91 int x; 92 while (head->next != NULL) //如果head不是尾节点,那么找到尾节点,并使head成为尾节点;尾节点指向NULL 93 head = head->next; 94 remove = head; //将head(尾节点)赋给remove,是remove也成为尾节点 95 cout << "请输入要插入的数据(-1结束): " << endl; 96 cin >> x; 97 while (x != -1) 98 { 99 node = (linkList*)malloc(sizeof(linkList)); 100 node->Element = x; 101 node->next = remove->next; //此处也可以使用head->next,但使用remove是为了循环大计 102 remove->next = node; //此处也可以使用head->next,但是链表就是断的了 103 remove = node; //为下一次循环做准备 104 cout << "请输入要插入的数据(-1结束): " << endl; 105 cin >> x; 106 } 107 } 108 109 /* 110 01)打印链表 111 02)输入为当前节点,或者是一个结构的地址 112 */ 113 114 void showlinkList(linkList** head) 115 { 116 if ((*head)->next == NULL ) //如果当前节点是头节点,则指向下一个节点;以为头节点是没有数据的 117 (*head) = (*head)->next; 118 (*head) = (*head)->next; //不显示头结点中的元素;上面那个if肯定不会被执行的,因为传入的参数是头结点,头结点的下一个节点肯定不是空(只要有数据) 119 while ((*head) != NULL ) 120 { 121 cout << (*head)->Element << endl; 122 (*head) = (*head)->next; 123 } 124 } 125 126 /* 127 01)删除第index个节点 128 */ 129 /*返回第index个节点*/ 130 linkList* indexof(linkList* head, int index) 131 { 132 linkList* p; 133 if (head->next == NULL) 134 { 135 cout << "输入的当前节点为头节点" << endl; 136 return NULL; 137 } 138 int j; 139 for (j = 1, p = head->next; p != NULL && j < index; j++) //如果index等于1,则该for循环会忽略p=NULL的情况 140 p = p->next; 141 return j == index ? p : NULL; 142 } 143 /*删除第index个节点*/ 144 int deleteElementByIndex(linkList* head, int index) 145 { 146 linkList* p; 147 linkList* temp; 148 p = indexof(head, index); //找到第index个节点 149 if (p == NULL) 150 { 151 cout << "要删除的为头节点" << endl; 152 return false; 153 } 154 temp = index == 1 ? NULL : indexof(head, index - 1); //找到要删除节点的前一个节点(前驱节点) 155 temp->next = p->next; //让要删除节点的前驱节点指向要删除节点的下一个节点 156 free(p); //释放要删除节点的内存 157 return true; 158 }
1 #include "stacktp1.h" 2 3 int main() 4 { 5 linkList* linkPtr; 6 initlinkList(&linkPtr); 7 insertDatabyHead(&linkPtr); 8 showlinkList(&linkPtr); 9 destroylinkListByHead(&linkPtr); 10 11 system("pause"); 12 return 0; 13 }
运行结果:
3、链表尾插入法工作流程及测试
3.1调试deleteElementByIndex()函数的发现,使用二阶指针是要谨慎的
调试deleteElementByIndex()函数发现,主函数中的linkPtr经过showlinkList()函数之后已经成为了NULL空指针,原因在于所有的函数都使用二阶指针,一旦子函数中对实参进行了赋值的操作,
那么主函数中的实参也是会发生变化的,如:05)二阶指针作为形参,一阶指针的地址作为实参,改变子涵数(形参)实参的值会影响实参的值,所以使用二阶指针需谨慎~
改进的方法:
01)将实参传递给子涵数中的一个一阶指针,然后改变该一阶指针,这样就不会改变实参,如 04)二阶指针作为形参,一阶指针的地址作为实参,将形参赋给子函数中的一个变量,改变该便利的值不会影响实参的值
02)将部分函数中的二阶形参该为一阶形参
下面附上全部子函数使用二阶指针的例子,该例子中的showlinkList()函数会使linPtr成为NULL空指针,导致接下来继续调用linkPtr出错
1 #include "stacktp1.h" 2 3 int main() 4 { 5 linkList* linkPtr; //创建链表的头结点 6 initlinkList(&linkPtr); 7 insertDatabyTail(&linkPtr); 8 //showlinkList(&linkPtr); //此句已经使linkPt成为了尾节点,所以执行下一句会报错 9 deleteElementByIndex(&linkPtr,5); //删除头结点为linkPtr,链表中第二个数 10 showlinkList(&linkPtr); //此句已经使linkPt成为了尾节点,所以不会执行下一个函数中的while循环,即不会删除链表 11 destroylinkListByHead(&linkPtr); 12 13 system("pause"); 14 return 0; 15 }
1 #include "stacktp1.h" 2 3 /* 4 01)链表的初始化(给头节点申请空间),所以在使用改函数的时候,只传入已创建结构的next指针即可; 5 02)使用指向指针的指针作为形参的原因在于要传入的参数是一个指针(要传入的参数是结构中的next指针), 6 如果形参定义为一层指针,那么就会发生形参改变而实参并没有发生任何改变的情况。 7 */ 8 void initlinkList(linkList** head) 9 { 10 if ((*head) == NULL) //如果结构这的next指针为空,那么表明已经初始化完成;刚刚这里写成了 != 导致在插入数据的时候head->next访问不了 11 return; 12 *head = (linkList*)malloc(sizeof(linkList)); //给结构分配足够的内存空间 13 (*head)->Element = 0; 14 (*head)->next = NULL; //head此时即是头节点也是尾节点,因为此时head指向了NULL 15 } 16 17 /* 18 01)链表的销毁 19 02)链表所有节点包括头结点都是动态申请的堆空间,使用完毕后必须手动释放,这里的销毁要把所有的节点空间全部释放; 20 03)方法一:从链表头开始遍历,也就是从前向后逐个释放每一个节点的空间 21 */ 22 void destroylinkListByHead(linkList** head) 23 { 24 linkList* header = *head; 25 linkList* p; 26 while ( header != NULL) //原来这里的条件是(*head)!=NULL,导致p->next指向了空指针;或者是循环了无数次 27 { 28 p = header; //讲当前数据节点保存下来 29 header = p->next; //将下一个数据节点保存下来 30 free(p); //讲当前节点空间释放 31 cout << "删除数据: " <Element << endl; 32 } 33 free(header); //释放头节点 34 head = NULL; //空间释放完的指针指向空指针 35 cout << "链表已被删除完毕" << endl; 36 } 37 38 /* 39 01)方法二:从链表尾部向头部开始销毁,就不用临时保存 40 02)用递归遍历到最后一个结点,逐层向上返回,销毁每一个节点,顺序就是从头尾向头结点的顺序销毁。 41 */ 42 void destroylinkListByTail(linkList** head) 43 { 44 if ((*head) == NULL) 45 return; 46 destroylinkListByTail(&((*head)->next)); //递归调用 47 free(*head); 48 head = NULL; 49 cout << "链表已被删除完毕" << endl; 50 } 51 52 /* 53 01)向链表这插入数据 54 02)头插法:每次在头结点H的后面插入一个输入的数据,链表中的数据顺序和实际输入顺序相反 55 03)插入的过程主要是:先申请一个新的结点,链表不像数组一次性分配指定长度的空间, 56 链表是需要增长一个就再申请一份,然后链接起来。申请完了之后给节点赋值,让新申请的节 57 点指向头结点的next,也就是node->next = h->next,再让头结点指向这个新节点, 58 H->next = node就完成插入操作。 59 04)传入的参数可以是头结点(头节点指向的是NULL) 60 05)传入的参数也可以是一个linkList结构的地址 61 06)以为此时是要向一个链表中插入数据,所以在使用malloc之后并没有释放内存 62 */ 63 void insertDatabyHead(linkList** head) 64 { 65 linkList* node; //新建一个需要插入的节点 66 int x; 67 cout << "请输入数据(-1结束):" << endl; 68 cin >> x; 69 while (x != -1) 70 { 71 node = (linkList*) malloc(sizeof(linkList)); //为需要插入的节点分配空间 72 node->Element = x; 73 //如果头节点指向的是NULL,所以下面这一句node->next换成NULL也可以 74 node->next = (*head)->next; //使node成为head指向的一个节点之后的节点 75 (*head)->next = node; //使 76 cout << "请输入数据(-1结束):" << endl; //接着输入数据 77 cin >> x; 78 } 79 } 80 81 /* 82 01)向链表这插入数据 83 02)尾插法:每次插入新的数据在链表的尾部插入就行,链表中的数据顺序和实际输入顺序相同 84 03)先找到链表的尾节点H->next == NULL,就是最后一个节点,同样插入就行。相比头插法, 85 尾插法插入数据的时候如果链表不是一条空链表,得遍历先找到尾节点。 86 04)传入的参数的头结点(头节点指向的是NULL) 87 05)输入的参数是一个linkList结构的地址 88 */ 89 90 void insertDatabyTail(linkList** head) 91 { 92 linkList* node; 93 linkList* remove; 94 int x; 95 while ((*head)->next != NULL) //如果head不是尾节点,那么找到尾节点,并使head成为尾节点;尾节点指向NULL 96 (*head) = (*head)->next; 97 remove = (*head); //将head(尾节点)赋给remove,是remove也成为尾节点 98 cout << "请输入要插入的数据(-1结束): " << endl; 99 cin >> x; 100 while (x != -1) 101 { 102 node = (linkList*)malloc(sizeof(linkList)); 103 node->Element = x; 104 node->next = remove->next; //此处也可以使用head->next,但使用remove是为了循环大计 105 remove->next = node; //此处也可以使用head->next,但是链表就是断的了 106 remove = node; //为下一次循环做准备 107 cout << "请输入要插入的数据(-1结束): " << endl; 108 cin >> x; 109 } 110 } 111 112 /* 113 01)打印链表 114 02)输入为当前节点,或者是一个结构的地址 115 */ 116 117 void showlinkList(linkList** head) 118 { 119 if ((*head)->next == NULL ) //如果当前节点是头节点,则指向下一个节点;以为头节点是没有数据的 120 (*head) = (*head)->next; 121 (*head) = (*head)->next; //不显示头结点中的元素;上面那个if肯定不会被执行的,因为传入的参数是头结点,头结点的下一个节点肯定不是空(只要有数据) 122 while ((*head) != NULL ) 123 { 124 cout << (*head)->Element << endl; 125 (*head) = (*head)->next; //注意!!这里已经将传入的头结点变成了NULL!!!!!且实参是头结点的地址,所以会改变实参的值 126 } 127 } 128 129 /* 130 01)删除第index个节点 131 */ 132 /*返回第index个节点*/ 133 linkList* indexof(linkList* head, int index) 134 { 135 linkList* p = head; 136 if (head->next == NULL) //(*head)->next == NULL 137 { 138 cout << "输入的链表只有一个节点,且该节点为尾节点" << endl; 139 return NULL; 140 } 141 int j; 142 for (j = 0; p != NULL && j < index; j++) //如果index等于1,则该for循环会忽略p=NULL的情况 143 p = p->next; 144 return j == index ? p : NULL; 145 } 146 /*删除第index个节点*/ 147 int deleteElementByIndex(linkList** head, int index) 148 { 149 linkList* p; 150 linkList* temp; 151 p = indexof(*head, index); //找到第index个节点 152 if (p == NULL) 153 { 154 cout << "要删除的为头节点" << endl; 155 return false; 156 } 157 temp = index == 1 ? NULL : indexof(*head, index - 1); //找到要删除节点的前一个节点(前驱节点) 158 temp->next = p->next; //让要删除节点的前驱节点指向要删除节点的下一个节点 159 free(p); //释放要删除节点的内存 160 return true; 161 }
1 /*链表*/ 2 #include3 4 using std::cin; 5 using std::cout; 6 using std::endl; 7 8 typedef int ElementType; 9 10 /*定义一个结构*/ 11 struct linkList 12 { 13 ElementType Element; //定义结构中的一个数据(数据域) 14 linkList* next; //定义指向下一个结构的指针(指针域) 15 }; 16 17 typedef struct linkList *PtrtoNode; //PtrtoNode是一个类型,可以定义变量,且PtrtoNode是一个指针,指向结构体Node 18 typedef PtrtoNode List; //为PtrtoNode起别名为List 19 typedef PtrtoNode Position; //为PtrtoNode起别名为Position 20 21 void initlinkList(linkList** head); 22 void destroylinkListByHead(linkList** head); 23 void destroylinkListByTail(linkList** head); 24 void insertDatabyHead(linkList** head); 25 void insertDatabyTail(linkList** head); 26 void showlinkList(linkList** head); 27 linkList* indexof(linkList** head, int index); 28 int deleteElementByIndex(linkList** head, int index);
运行结果:
3.2 改进(将showlinkList()中定义一个变量a,将形参赋值给变量a,改变变量a,而不改变实参即可)
如下图所示:
另外在destroylinkListByHead()函数中的while循环中加入了一个cout用于显示删除的数据
1 #include "stacktp1.h" 2 3 int main() 4 { 5 linkList* linkPtr; //创建链表的头结点 6 initlinkList(&linkPtr); 7 insertDatabyTail(&linkPtr); 8 showlinkList(&linkPtr); //此句已经使linkPt成为了尾节点,所以执行下一句会报错 9 deleteElementByIndex(&linkPtr,5); //删除头结点为linkPtr,链表中第二个数 10 showlinkList(&linkPtr); //此句已经使linkPt成为了尾节点,所以不会执行下一个函数中的while循环,即不会删除链表 11 destroylinkListByHead(&linkPtr); 12 13 system("pause"); 14 return 0; 15 }
1 #include "stacktp1.h" 2 3 /* 4 01)链表的初始化(给头节点申请空间),所以在使用改函数的时候,只传入已创建结构的next指针即可; 5 02)使用指向指针的指针作为形参的原因在于要传入的参数是一个指针(要传入的参数是结构中的next指针), 6 如果形参定义为一层指针,那么就会发生形参改变而实参并没有发生任何改变的情况。 7 */ 8 void initlinkList(linkList** head) 9 { 10 if ((*head) == NULL) //如果结构这的next指针为空,那么表明已经初始化完成;刚刚这里写成了 != 导致在插入数据的时候head->next访问不了 11 return; 12 *head = (linkList*)malloc(sizeof(linkList)); //给结构分配足够的内存空间 13 (*head)->Element = 0; 14 (*head)->next = NULL; //head此时即是头节点也是尾节点,因为此时head指向了NULL 15 } 16 17 /* 18 01)链表的销毁 19 02)链表所有节点包括头结点都是动态申请的堆空间,使用完毕后必须手动释放,这里的销毁要把所有的节点空间全部释放; 20 03)方法一:从链表头开始遍历,也就是从前向后逐个释放每一个节点的空间 21 */ 22 void destroylinkListByHead(linkList** head) 23 { 24 linkList* header = (*head)->next; //由于传入的是头结点,所以越过头结点,保留头结点 25 linkList* p; 26 cout << "开始删除链表" << endl; 27 while ( header != NULL) //原来这里的条件是(*head)!=NULL,导致p->next指向了空指针;或者是循环了无数次 28 { 29 p = header; //讲当前数据节点保存下来 30 cout << "删除数据: " << p->Element << endl; 31 header = p->next; //将下一个数据节点保存下来 32 free(p); //讲当前节点空间释放 33 } 34 free(header); //释放头节点 35 head = NULL; //空间释放完的指针指向空指针 36 cout << "链表已被删除完毕" << endl; 37 } 38 39 /* 40 01)方法二:从链表尾部向头部开始销毁,就不用临时保存 41 02)用递归遍历到最后一个结点,逐层向上返回,销毁每一个节点,顺序就是从头尾向头结点的顺序销毁。 42 */ 43 void destroylinkListByTail(linkList** head) 44 { 45 if ((*head) == NULL) 46 return; 47 destroylinkListByTail(&((*head)->next)); //递归调用 48 free(*head); 49 head = NULL; 50 cout << "链表已被删除完毕" << endl; 51 } 52 53 /* 54 01)向链表这插入数据 55 02)头插法:每次在头结点H的后面插入一个输入的数据,链表中的数据顺序和实际输入顺序相反 56 03)插入的过程主要是:先申请一个新的结点,链表不像数组一次性分配指定长度的空间, 57 链表是需要增长一个就再申请一份,然后链接起来。申请完了之后给节点赋值,让新申请的节 58 点指向头结点的next,也就是node->next = h->next,再让头结点指向这个新节点, 59 H->next = node就完成插入操作。 60 04)传入的参数可以是头结点(头节点指向的是NULL) 61 05)传入的参数也可以是一个linkList结构的地址 62 06)以为此时是要向一个链表中插入数据,所以在使用malloc之后并没有释放内存 63 */ 64 void insertDatabyHead(linkList** head) 65 { 66 linkList* node; //新建一个需要插入的节点 67 int x; 68 cout << "请输入数据(-1结束):" << endl; 69 cin >> x; 70 while (x != -1) 71 { 72 node = (linkList*) malloc(sizeof(linkList)); //为需要插入的节点分配空间 73 node->Element = x; 74 //如果头节点指向的是NULL,所以下面这一句node->next换成NULL也可以 75 node->next = (*head)->next; //使node成为head指向的一个节点之后的节点 76 (*head)->next = node; //使 77 cout << "请输入数据(-1结束):" << endl; //接着输入数据 78 cin >> x; 79 } 80 } 81 82 /* 83 01)向链表这插入数据 84 02)尾插法:每次插入新的数据在链表的尾部插入就行,链表中的数据顺序和实际输入顺序相同 85 03)先找到链表的尾节点H->next == NULL,就是最后一个节点,同样插入就行。相比头插法, 86 尾插法插入数据的时候如果链表不是一条空链表,得遍历先找到尾节点。 87 04)传入的参数的头结点(头节点指向的是NULL) 88 05)输入的参数是一个linkList结构的地址 89 */ 90 91 void insertDatabyTail(linkList** head) 92 { 93 linkList* node; 94 linkList* remove; 95 int x; 96 while ((*head)->next != NULL) //如果head不是尾节点,那么找到尾节点,并使head成为尾节点;尾节点指向NULL 97 (*head) = (*head)->next; 98 remove = (*head); //将head(尾节点)赋给remove,是remove也成为尾节点 99 cout << "请输入要插入的数据(-1结束): " << endl; 100 cin >> x; 101 while (x != -1) 102 { 103 node = (linkList*)malloc(sizeof(linkList)); 104 node->Element = x; 105 node->next = remove->next; //此处也可以使用head->next,但使用remove是为了循环大计 106 remove->next = node; //此处也可以使用head->next,但是链表就是断的了 107 remove = node; //为下一次循环做准备 108 cout << "请输入要插入的数据(-1结束): " << endl; 109 cin >> x; 110 } 111 } 112 113 /* 114 01)打印链表 115 02)输入为当前节点,或者是一个结构的地址 116 */ 117 118 void showlinkList(linkList** head) 119 { 120 linkList* header = *head; 121 if (header->next == NULL ) //如果当前节点是头节点,则指向下一个节点;以为头节点是没有数据的 122 header = header->next; 123 header = header->next; //不显示头结点中的元素;上面那个if肯定不会被执行的,因为传入的参数是头结点,头结点的下一个节点肯定不是空(只要有数据) 124 while (header != NULL ) 125 { 126 cout << header->Element << endl; 127 header = header->next; //注意!!这里已经将传入的头结点变成了NULL!!!!!且实参是头结点的地址,所以会改变实参的值 128 } 129 } 130 131 /* 132 01)删除第index个节点 133 */ 134 /*返回第index个节点*/ 135 linkList* indexof(linkList* head, int index) 136 { 137 linkList* p = head; 138 if (head->next == NULL) //(*head)->next == NULL 139 { 140 cout << "输入的链表只有一个节点,且该节点为尾节点" << endl; 141 return NULL; 142 } 143 int j; 144 for (j = 0; p != NULL && j < index; j++) //如果index等于1,则该for循环会忽略p=NULL的情况 145 p = p->next; 146 return j == index ? p : NULL; 147 } 148 /*删除第index个节点*/ 149 int deleteElementByIndex(linkList** head, int index) 150 { 151 linkList* p; 152 linkList* temp; 153 p = indexof(*head, index); //找到第index个节点 154 if (p == NULL) 155 { 156 cout << "要删除的为头节点" << endl; 157 return false; 158 } 159 temp = index == 1 ? NULL : indexof(*head, index - 1); //找到要删除节点的前一个节点(前驱节点) 160 temp->next = p->next; //让要删除节点的前驱节点指向要删除节点的下一个节点 161 cout << "开始删除第" << index << "个节点" << endl; 162 free(p); //释放要删除节点的内存 163 return true; 164 }
1 /*链表*/ 2 #include3 4 using std::cin; 5 using std::cout; 6 using std::endl; 7 8 typedef int ElementType; 9 10 /*定义一个结构*/ 11 struct linkList 12 { 13 ElementType Element; //定义结构中的一个数据(数据域) 14 linkList* next; //定义指向下一个结构的指针(指针域) 15 }; 16 17 typedef struct linkList *PtrtoNode; //PtrtoNode是一个类型,可以定义变量,且PtrtoNode是一个指针,指向结构体Node 18 typedef PtrtoNode List; //为PtrtoNode起别名为List 19 typedef PtrtoNode Position; //为PtrtoNode起别名为Position 20 21 void initlinkList(linkList** head); 22 void destroylinkListByHead(linkList** head); 23 void destroylinkListByTail(linkList** head); 24 void insertDatabyHead(linkList** head); 25 void insertDatabyTail(linkList** head); 26 void showlinkList(linkList** head); 27 linkList* indexof(linkList* head, int index); 28 int deleteElementByIndex(linkList** head, int index);
执行结果:
4、优先队列(堆)测试
4.1++i和i++运算符的测试
1 #include2 3 using std::cout; 4 using std::endl; 5 6 int main() 7 { 8 int i = 8; 9 int arr[] = { 1,2,3,4,5,6,7,8,9 }; 10 11 //cout << arr[i--] << endl; //打印9 先使用后修改 12 //cout << arr[i--] << endl; //打印8 13 14 cout << arr[--i] << endl; //打印8 先修改后使用 15 cout << arr[--i] << endl; //打印7 16 cout << endl; 17 cout << i; //此时i=6 18 19 system("pause"); 20 return 0; 21 }