数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试...

目录

1、二阶指针做为形参的目的(使实参的值发生改变)

2、链表头插入法工作流程及测试

3、链表尾插入法工作流程及测试

    3.1调试deleteElementByIndex()函数发现,主函数中的linkPtr经过showlinkList()函数之后已经成为了NULL空指针

    3.2改进

4、优先队列(堆)测试

  4.1++i和i++运算符的测试

1、二阶指针作为形参的目的

1)普通变量做为形参,不能改变主函数中实参的值

例子:

 1 //01)形参改变,实参并没有改变的例子
 2 #include 
 3 
 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 }
普通变量作为形参和普通变了作为实参

运行结果:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第1张图片

2)一阶指针作为形参,变量地址作为实参可以改变主函数中实参的值

例子:

 1 //02)实参发生改变的例子
 2 #include 
 3 
 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 }
View Code

运行结果:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第2张图片

3)二阶指针做为形参,对指针用取地址运算符取地址后作为实参,可以改变实参的值

例子:

 1 #include 
 2 
 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 */
View Code

运行结果:

在例子中的一个解析:

 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  
View Code

参考博客:https://blog.csdn.net/qq_34991245/article/details/81868212

04)二阶指针作为形参,一阶指针的地址作为实参,将形参赋给子函数中的一个变量,后改变该变量的值不会影响实参的值

例子:

 1 #include 
 2 
 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 }
View Code

运行结果:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第3张图片

05)二阶指针作为形参,一阶指针的地址作为实参,改变子涵数(形参)实参的值会影响实参的值

 1 #include 
 2 
 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 }
View Code

运行结果:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第4张图片

2、链表头插入法工作流程及测试

01)头插法工作流程:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第5张图片

 1 /*链表*/
 2 
 3 #include 
 4 
 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);
linkList.h
  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 }
linkList.cpp
 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 }
main.cpp

运行结果:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第6张图片

3、链表尾插入法工作流程及测试

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第7张图片

3.1调试deleteElementByIndex()函数的发现,使用二阶指针是要谨慎的

调试deleteElementByIndex()函数发现,主函数中的linkPtr经过showlinkList()函数之后已经成为了NULL空指针,原因在于所有的函数都使用二阶指针,一旦子函数中对实参进行了赋值的操作,

那么主函数中的实参也是会发生变化的,如:05)二阶指针作为形参,一阶指针的地址作为实参,改变子涵数(形参)实参的值会影响实参的值,所以使用二阶指针需谨慎~

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第8张图片

改进的方法:

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 }
mian.cpp
  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 }
stack1.cpp
 1 /*链表*/
 2 #include 
 3 
 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);
stack1.h

运行结果:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第9张图片

 3.2 改进(将showlinkList()中定义一个变量a,将形参赋值给变量a,改变变量a,而不改变实参即可)

如下图所示:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第10张图片

另外在destroylinkListByHead()函数中的while循环中加入了一个cout用于显示删除的数据

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第11张图片

 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 }
mian.cpp
  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 }
stack1.cpp
 1 /*链表*/
 2 #include 
 3 
 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);
stack1.h

执行结果:

数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试..._第12张图片

4、优先队列(堆)测试

4.1++i和i++运算符的测试

 1 #include 
 2 
 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 }
示例

 

转载于:https://www.cnblogs.com/YiYA-blog/p/11559079.html

你可能感兴趣的:(数据结构与算法分析_二阶指针做为形参_链表头插入法工作流程及测试_链表尾插入法工作流程及测试_优先队列(堆)测试...)