• 30.链表练习题(1)(王道2023数据结构2.3.7节1-15题)


    【前面使用的所有链表的定义在第29节】

    试题1:

    设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。

    首先来看非递归算法,暴力遍历:

    1. int Del(LinkList &L,ElemType x){
    2. //此函数实现删除链表中为x的元素
    3. LNode *p,*q;
    4. p = L; //p指向头结点
    5. q = L->next; //q指向首元结点
    6. while(q->next!=NULL){
    7. if(q->data == x){
    8. p->next = q->next;
    9. free(q);
    10. q = p->next;
    11. }
    12. else{
    13. p = p->next;
    14. q = q->next;
    15. }
    16. }
    17. return 0;
    18. }
    19. int main(){
    20. LinkList L;
    21. InitList(L);
    22. Create(L);
    23. PrintList(L);
    24. Del(L, 3);
    25. PrintList(L);
    26. return 0;
    27. }

    然后看递归算法:

    1. int Del(LinkList &L,ElemType x){
    2. //此函数实现递归删除链表中为x的元素
    3. LNode *p;
    4. if(L==NULL)
    5. return 0;
    6. if(L->data==x){
    7. p = L;
    8. L = L->next;
    9. free(p);
    10. Del(L, x);
    11. }
    12. else{
    13. Del(L->next, x);
    14. }
    15. return 0;
    16. }
    17. int main(){
    18. LinkList L;
    19. InitList(L);
    20. Create(L);
    21. PrintList(L);
    22. Del(L, 3);
    23. PrintList(L);
    24. return 0;
    25. }

    这里分析一下怎么递归的:从头开始,如果首元结点就是被删掉的x那很显然;如果不是,这时候执行Del(L->next, x),如果第2个是被删掉的元素,进入if(L->next->data==x)分支,此时p=L实际上执行p=L->next,p指向了第2个被删除的结点;第2行L = L->next执行的是L->next=L->next->next,把首元结点的next域修改为指向第3个结点;然后free(p)释放空间,最后继续执行Del(L,x)相当于Del(L->next, x),这时L->next是第3个结点,依次递归。

    试题2:(与题1差不多)

    试题3:L是带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值

    此题可以使用栈来解决,这里主要提书上的递归方法:

    1. void AdversePrint(LinkList L){
    2. //此函数实现反向打印链表L的元素
    3. if(L!=NULL){
    4. AdversePrint(L->next); //递归
    5. printf("[%d] ", L->data); //打印当前元素
    6. }
    7. }
    8. int main(){
    9. LinkList L;
    10. InitList(L);
    11. Create(L);
    12. PrintList(L);
    13. AdversePrint(L->next); //带头结点的链表,这里L->next指向首元结点
    14. return 0;
    15. }

    试题4:编写在带头结点的单链表L中删除一个最小值结点的高效算法。

    (注意不要写野指针!)

    1. void Deletexmin(LinkList &L){
    2. //此函数实现删除链表最小值元素
    3. LNode *p, *q, *m, *n;
    4. int a;
    5. p = L; //p指向头结点
    6. q = L->next; //q指向首元结点
    7. a = q->data; //首元结点元素赋给a
    8. m = p;
    9. n = q;
    10. while (q->next != NULL){
    11. q = q->next; //指针后移
    12. p = p->next; //指针后移
    13. if(q->data < a){
    14. a = q->data; //更新a
    15. m = p; //记录当前最小值的前驱结点
    16. n = q; //记录当前最小值结点
    17. }
    18. }
    19. m->next = n->next;
    20. free(n);
    21. }
    22. int main(){
    23. LinkList L;
    24. InitList(L);
    25. Create(L);
    26. PrintList(L);
    27. Deletexmin(L);
    28. PrintList(L);
    29. return 0;
    30. }

    试题5:将单链表的元素就地逆置

    利用头插法重新建立链表:

    1. void ReverseL(LinkList &L){
    2. //此函数实现逆置单链表所有元素
    3. LNode *p, *q;
    4. p = L;
    5. q = L->next; //q指向首元结点
    6. p = q;
    7. q = q->next; //q指向第二个结点
    8. p->next = NULL;
    9. if(q==NULL)
    10. return ;
    11. else{
    12. while (q!=NULL){
    13. p = q->next; //p移向首元结点
    14. q->next = L->next;
    15. L->next = q; //修改头结点指针,头插法
    16. q = p;
    17. }
    18. }
    19. }
    20. int main(){
    21. LinkList L;
    22. InitList(L);
    23. Create(L);
    24. PrintList(L);
    25. ReverseL(L);
    26. PrintList(L);
    27. return 0;
    28. }

    试题6:有一个带头结点的单链表L,设计算法使其递增有序。

    采用插入排序的思想:

    1. void SortL(LinkList &L){
    2. //此函数实现递增排序单链表所有元素
    3. LNode *p, *q, *r;
    4. p = L->next; //p工作在排好序的链表
    5. q = p->next; //q是待排序元素,即第二个结点,断开之后的第一个结点
    6. r = q->next; //r在q之后
    7. p->next = NULL; //断开
    8. while(q!=NULL){
    9. r = q->next;
    10. p = L;
    11. while(q->data > p->next->data && p->next!=NULL){
    12. p = p->next;
    13. }
    14. q->next = p->next;
    15. p->next = q;
    16. q = r;
    17. }
    18. }
    19. int main(){
    20. LinkList L;
    21. InitList(L);
    22. Create(L);
    23. PrintList(L);
    24. SortL(L);
    25. PrintList(L);
    26. return 0;
    27. }

    输出:

    1. 请输入要输入元素的个数:5
    2. 请输入第1元素的值:5
    3. 请输入第2元素的值:3
    4. 请输入第3元素的值:1
    5. 请输入第4元素的值:2
    6. 请输入第5元素的值:4
    7. 当前单链表的所有元素:[5] [3] [1] [2] [4]
    8. 当前单链表的所有元素:[1] [2] [3] [4] [5]

    试题7:(修改试题1的参数条件if(q->data == x)即可)

    试题8:求两个链表的公共链表

    如果有公共链表,一定是这样的Y型拓扑结构:

    1. int LengthL(LinkList L){
    2. //此函数实现求解链表长度
    3. LNode *p;
    4. p = L;
    5. int i = 0;
    6. while(p->next!=NULL){
    7. p = p->next;
    8. i++;
    9. }
    10. return i;
    11. }
    12. LinkList Search_1st_common(LinkList L1,LinkList L2){
    13. //此函数实现查找两个链表的公共结点
    14. int L1length = LengthL(L1);
    15. int L2length = LengthL(L2);
    16. printf("%d", LengthL(L1));
    17. printf("%d", LengthL(L2));
    18. int delta = 0; //记录长链表比短链表多多少
    19. LNode *longList, *shortList;
    20. if(L1length > L2length){
    21. longList = L1->next; //L1更长,longList指向L1首元结点
    22. shortList = L2->next;
    23. delta = L1length - L2length;
    24. }
    25. else{
    26. longList = L2->next; //L2更长,longList指向L2首元结点
    27. shortList = L1->next;
    28. delta = L2length - L1length;
    29. }
    30. printf("%d", longList->data);
    31. printf("%d", shortList->data);
    32. printf("%d", delta);
    33. for (int i = 0; i < delta;i++){ // 移动longList指针至delta位置
    34. longList = longList->next;
    35. }
    36. printf("%d", longList->data);
    37. //经过以上移动,此时longList指针与shortList指针指的剩余长度一样
    38. while (longList!=shortList && longList != NULL){
    39. longList = longList->next;
    40. shortList = shortList->next;
    41. }
    42. return longList;
    43. }
    44. void PrintResult(LinkList L){
    45. if(L == NULL)
    46. printf("公共结点不存在!");
    47. else
    48. PrintList(L);
    49. }
    50. int main(){
    51. LinkList L1,L2;
    52. InitList(L1);
    53. Create(L1);
    54. PrintList(L1);
    55. InitList(L2);
    56. Create(L2);
    57. PrintList(L2);
    58. PrintResult(Search_1st_common(L1, L2));
    59. return 0;
    60. }

    输出:

    1. 请输入要输入元素的个数:2
    2. 请输入第1元素的值:1
    3. 请输入第2元素的值:2
    4. 当前单链表的所有元素:[1] [2]
    5. 请输入要输入元素的个数:5
    6. 请输入第1元素的值:3
    7. 请输入第2元素的值:4
    8. 请输入第3元素的值:5
    9. 请输入第4元素的值:6
    10. 请输入第5元素的值:7
    11. 当前单链表的所有元素:[3] [4] [5] [6] [7]
    12. 253136公共结点不存在!
    13. 请输入要输入元素的个数:2
    14. 请输入第1元素的值:1
    15. 请输入第2元素的值:2
    16. 当前单链表的所有元素:[1] [2]
    17. 请输入要输入元素的个数:3
    18. 请输入第1元素的值:3
    19. 请输入第2元素的值:4
    20. 请输入第3元素的值:5
    21. 当前单链表的所有元素:[3] [4] [5]
    22. 233114公共结点不存在!

    在写这段代码的时候踩了一个坑:大家观察下面两段代码:

    1. void Search_1st_common(){
    2. //此函数实现查找两个链表的公共结点
    3. int L1length = 3;
    4. int L2length = 5;
    5. int delta = 0; //记录长链表比短链表多多少
    6. if(L1length > L2length){
    7. delta = L1length - L2length;
    8. }
    9. else{
    10. delta = L2length - L1length;
    11. }
    12. printf("%d", delta);
    13. }
    14. int main(){
    15. Search_1st_common();
    16. return 0;
    17. }
    1. void Search_1st_common(){
    2. //此函数实现查找两个链表的公共结点
    3. int L1length = 3;
    4. int L2length = 5;
    5. int delta = 0; //记录长链表比短链表多多少
    6. if(L1length > L2length){
    7. int delta = L1length - L2length;
    8. }
    9. else{
    10. int delta = L2length - L1length;
    11. }
    12. printf("%d", delta);
    13. }
    14. int main(){
    15. Search_1st_common();
    16. return 0;
    17. }

    打印delta的结果分别是2和0.后一段代码中if...else...结构体内相当于重新定义了一个delta,当结构体执行结束后又被释放掉了,所以delta的返回结果是0.

    试题9:试写出算法:按递增次序输出单链表中各节点的数据元素,并释放结点所占的内存空间。

    此题可以仿照试题4.不多解释。

    1. void Printelement(LinkList &L){
    2. //此函数实现按递增次序输出单链表数据元素,并释放结点所占的存储空间
    3. LNode *p, *q; // q记录了每次遍历的最小值结点的前驱
    4. int min;
    5. while(L->next!=NULL){ //最后结束条件是L删成空
    6. p = L;
    7. q = L;
    8. min = p->next->data; //第1个元素
    9. while(p->next != NULL){
    10. if(p->next->data < min){
    11. min = p->next->data;
    12. q = p;
    13. }
    14. p = p->next;
    15. }
    16. printf("[%d]", q->next->data);
    17. p = q->next;
    18. q->next = p->next;
    19. free(p);
    20. }
    21. }
    22. int main(){
    23. LinkList L1;
    24. InitList(L1);
    25. Create(L1);
    26. PrintList(L1);
    27. Printelement(L1);
    28. return 0;
    29. }

    试题10:将一个链表A分解成两个链表A和B,使得A表中含有原表中序号是奇数的元素,B表中含有原表中序号是偶数的元素。

    这里不采用书上写的设置访问序号变量。由于有周期性,适当控制指针即可:

    1. void Printelement(LinkList &L){
    2. //此函数实现将一个链表A分解成两个链表A和B,使得A表中含有原表中序号是奇数的元素,B表中含有原表中序号是偶数的元素。
    3. LNode *p, *q, *r; //r指针工作在新链表中
    4. LinkList L0; //L0相当于题目中的B
    5. InitList(L0);
    6. p = L->next;
    7. q = p->next; //q指向第2个结点
    8. r = L0;
    9. while(p!=NULL&&q!=NULL){
    10. r->next = q;
    11. p->next = q->next;
    12. if(p->next == NULL)
    13. break;
    14. p = p->next;
    15. if(q->next == NULL)
    16. break;
    17. q = p->next;
    18. r = r->next;
    19. r->next = NULL;
    20. }
    21. PrintList(L);
    22. PrintList(L0);
    23. }
    24. int main(){
    25. LinkList L1;
    26. InitList(L1);
    27. Create(L1);
    28. PrintList(L1);
    29. Printelement(L1);
    30. return 0;
    31. }

    输出:

    1. 当前单链表的所有元素:[1] [2] [3] [4] [5] [6]
    2. 当前单链表的所有元素:[1] [3] [5]
    3. 当前单链表的所有元素:[2] [4] [6]
    4. 当前单链表的所有元素:[1] [2] [3] [4] [5] [6] [7]
    5. 当前单链表的所有元素:[1] [3] [5] [7]
    6. 当前单链表的所有元素:[2] [4] [6]

    试题11:设C=\left \{ a_1,b_1,a_2,b_2...a_n,b_n \right \}为线性表,采用带头结点的hc单链表存放,设计一个就地算法,将其拆分成两个线性表,使得A=\left \{ a_1,a_2...a_n \right \}B=\left \{ b_n,b_{n-1}...b_1 \right \}

    此题跟第10题很像,修改指针即可。

    1. void Printelement(LinkList &L){
    2. //此函数实现将一个链表A分解成两个链表A和B,使得A表中含有原表中序号是奇数的元素,B表中含有原表中序号是偶数倒序的元素。
    3. LNode *p, *q, *r; //r指针工作在新链表中
    4. LinkList L0; //L0相当于题目中的B
    5. InitList(L0);
    6. p = L->next;
    7. q = p->next; //q指向第2个结点
    8. r = L0->next;
    9. while(p!=NULL&&q!=NULL){
    10. L0->next = q; //头插法
    11. p->next = q->next;
    12. q->next = r;
    13. if(p->next == NULL)
    14. break;
    15. p = p->next;
    16. if(p->next == NULL)
    17. break;
    18. q = p->next;
    19. r = L0->next;
    20. }
    21. PrintList(L);
    22. PrintList(L0);
    23. }
    24. int main(){
    25. LinkList L1;
    26. InitList(L1);
    27. Create(L1);
    28. PrintList(L1);
    29. Printelement(L1);
    30. return 0;
    31. }

    输出:

    1. 当前单链表的所有元素:[1] [2] [3] [4] [5] [6]
    2. 当前单链表的所有元素:[1] [3] [5]
    3. 当前单链表的所有元素:[6] [4] [2]

    试题12:在一个递增有序的单链表中,删除所有数值相同的元素,使表中不再有重复的元素。

    1. void DeleteSame(LinkList &L){
    2. //此函数实现把递增单链表的重复元素删除
    3. LNode *p, *q;
    4. p = L->next; //第一个元素
    5. q = p->next;
    6. while(q!=NULL){
    7. if(p->data == q->data){
    8. q = q->next;
    9. free(p->next);
    10. p->next = q;
    11. }
    12. else{
    13. p = p->next;
    14. q = q->next;
    15. }
    16. }
    17. }
    18. int main(){
    19. LinkList L1;
    20. InitList(L1);
    21. Create(L1);
    22. PrintList(L1);
    23. DeleteSame(L1);
    24. PrintList(L1);
    25. return 0;
    26. }

    输出:

    1. 当前单链表的所有元素:[1] [2] [2] [3] [3] [4] [4] [5]
    2. 当前单链表的所有元素:[1] [2] [3] [4] [5]

    试题13:假设有两个按元素值递增次序排列的线性表,以单链表形式存储。编写算法把这两个单链表归并为一个按元素值递减的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。

    此题属于简单题,最后使用头插法倒序即可。

    1. LinkList Merge(LinkList L1,LinkList L2){
    2. //此函数实现把两个递增的单链表合并成递减的单链表
    3. LNode *p, *q; //L1指针
    4. LNode *m, *n; //L2指针
    5. LNode *r; //新表,头插法
    6. LinkList L;
    7. InitList(L); //初始化
    8. p = L1->next; //L1第1个结点
    9. m = L2->next; //L2第1个结点
    10. q = p->next; //L1第2个结点
    11. n = m->next; //L2第2个结点
    12. r = L->next;
    13. while(L1->next!=NULL && L2->next!=NULL){
    14. if(p->data < m->data){
    15. L->next = p;
    16. p->next = r;
    17. p = q;
    18. r = L->next;
    19. if(q == NULL)
    20. break;
    21. q = q->next;
    22. }
    23. else{
    24. L->next = m;
    25. m->next = r;
    26. m = n;
    27. r = L->next;
    28. if(n == NULL)
    29. break;
    30. n = n->next;
    31. }
    32. }
    33. if(L1->next == NULL){
    34. while(L2->next!=NULL){
    35. L->next = m;
    36. m->next = r;
    37. m = n;
    38. r = L->next;
    39. if(n == NULL)
    40. break;
    41. n = n->next;
    42. }
    43. }
    44. else{
    45. while(L1->next!=NULL){
    46. L->next = p;
    47. p->next = r;
    48. p = q;
    49. r = L->next;
    50. if(q == NULL)
    51. break;
    52. q = q->next;
    53. }
    54. }
    55. return L;
    56. }
    57. int main(){
    58. LinkList L1, L2;
    59. InitList(L1);
    60. Create(L1);
    61. PrintList(L1);
    62. InitList(L2);
    63. Create(L2);
    64. PrintList(L2);
    65. PrintList(Merge(L1,L2));
    66. return 0;
    67. }

    输出:

    1. 请输入要输入元素的个数:3
    2. 请输入第1元素的值:1
    3. 请输入第2元素的值:2
    4. 请输入第3元素的值:4
    5. 当前单链表的所有元素:[1] [2] [4]
    6. 请输入要输入元素的个数:2
    7. 请输入第1元素的值:1
    8. 请输入第2元素的值:2
    9. 当前单链表的所有元素:[1] [2]
    10. 当前单链表的所有元素:[4] [2] [2] [1] [1]

    试题14:设A和B是两个单链表(带头结点),其中元素递增有序,设计一个算法从A和B中公共元素产生单链表C,要求不破坏A,B的结点。

    1. LinkList Intersection(LinkList L1,LinkList L2){
    2. //此函数实现产生单链表,包含两个链表的公共元素
    3. LNode *p; //L1指针
    4. LNode *q; //L2指针
    5. LNode *r; //新表的指针
    6. LinkList L;
    7. InitList(L);
    8. p = L1->next;
    9. q = L2->next;
    10. r = L;
    11. while(p!=NULL && q!=NULL){
    12. if(p->data < q->data){
    13. p = p->next;
    14. }
    15. else if(p->data = q->data){
    16. LNode *s = (LNode *)malloc(sizeof(LNode));
    17. s->data = p->data;
    18. s->next = NULL;
    19. r->next = s;
    20. r = r->next;
    21. p = p->next; //后移
    22. q = q->next;
    23. }
    24. else{
    25. q = q->next;
    26. }
    27. }
    28. return L;
    29. }
    30. int main(){
    31. LinkList L1, L2;
    32. InitList(L1);
    33. Create(L1);
    34. PrintList(L1);
    35. InitList(L2);
    36. Create(L2);
    37. PrintList(L2);
    38. PrintList(Intersection(L1,L2));
    39. return 0;
    40. }

    输出:

    1. 请输入要输入元素的个数:4
    2. 请输入第1元素的值:1
    3. 请输入第2元素的值:2
    4. 请输入第3元素的值:3
    5. 请输入第4元素的值:4
    6. 当前单链表的所有元素:[1] [2] [3] [4]
    7. 请输入要输入元素的个数:4
    8. 请输入第1元素的值:2
    9. 请输入第2元素的值:3
    10. 请输入第3元素的值:4
    11. 请输入第4元素的值:5
    12. 当前单链表的所有元素:[2] [3] [4] [5]
    13. 当前单链表的所有元素:[2] [3] [4]

    试题15:此题要求同14,但要求合并链表存储在A中。

    1. LinkList Intersection(LinkList L1,LinkList L2){
    2. //此函数实现产生单链表,包含两个链表的公共元素
    3. LNode *p, *m; // L1指针,m保存待删结点的前驱,p待删结点
    4. LNode *q; //L2指针
    5. m = L1;
    6. p = L1->next;
    7. q = L2->next;
    8. while(p!=NULL && q!=NULL){
    9. if(p->data < q->data){
    10. m->next = p->next;
    11. free(p);
    12. p = m->next;
    13. }
    14. else if(p->data == q->data){
    15. p = p->next;
    16. q = q->next;
    17. m = m->next;
    18. }
    19. else{
    20. q = q->next;
    21. }
    22. }
    23. if(q == NULL){
    24. while(p!=NULL){
    25. m->next = p->next;
    26. free(p);
    27. p = m->next;
    28. }
    29. }
    30. return L1;
    31. }
    32. int main(){
    33. LinkList L1, L2;
    34. InitList(L1);
    35. Create(L1);
    36. PrintList(L1);
    37. InitList(L2);
    38. Create(L2);
    39. PrintList(L2);
    40. PrintList(Intersection(L1,L2));
    41. return 0;
    42. }

    输出:

    1. 当前单链表的所有元素:[3] [5] [7] [9]
    2. 当前单链表的所有元素:[4] [5] [8] [9]
    3. 当前单链表的所有元素:[5] [9]
    4. 当前单链表的所有元素:[2] [3] [4] [5]
    5. 当前单链表的所有元素:[1] [2] [3] [4]
    6. 当前单链表的所有元素:[2] [3] [4]
    7. 当前单链表的所有元素:[5] [6] [7] [8]
    8. 当前单链表的所有元素:[1] [2] [3] [4]
    9. 当前单链表的所有元素:
  • 相关阅读:
    Java中控制多线程顺序执行
    Pytest系列-快速入门和基础讲解(1)
    MyBatis 快速入门
    nacos配置mysql死活报 Nacos No DataSource set异常解决
    WiFi无线通信技术详解
    【云原生--Kubernetes】Pod容器与镜像拉取策略
    Elasticsearch集群搭建 + ELFK数据传输链路打通
    MySQL数据库备份全攻略:从基础到高级,一文掌握所有备份技巧
    numpy的多项式函数: `poly1d`
    java中如何将嵌套循环性能提高500倍
  • 原文地址:https://blog.csdn.net/qq_54708219/article/details/132996994