链表常见例题

在各大考试中,链表是一个十分重要的考点,本篇博客将着重介绍几道常见的关于链表的例题。

  1. 从尾到头打印链表
  2. 删除一个无头单链表的非尾节点(不能遍历链表)
  3. 在一个无头单链表的一个节点前插入一个节点(不能遍历链表)
  4. 单链表实现约瑟夫环
  5. 逆置/反转单链表
  6. 单链表冒泡排序
  7. 合并两个有序链表
  8. 找到链表的中间节点
  9. 查找单链表倒数第k个节点(要求只能遍历一遍链表)
  10. 删除链表的倒数第k个节点
  11. 判断单链表是否带环,带环返回相遇点的指针
  12. 求环的入口点
  13. 若链表带环,求环的长度
  14. 判断两个链表是否相交,并求出交点
  15. 求两个已排序单链表中相同的数据

    我们来一道一道的解决问题


1. 从尾到头打印链表


分析:利用递归的思想就可以实现从尾到头打印单链表
代码:

void LinkListPrintReverse(LinkNode* phead){
    if(phead == NULL){
    //空链表
        return;
    }
    LinkListPrintReverse(phead->next);
    printf("%c  ",phead->data);
    return;
}

2.删除一个无头单链表的非尾节点(不能遍历链表)


分析:将要删除节点的下一个节点的值赋给要删除的节点,再删除下一个节点,这样就避免了单链表不遍历链表就找不到前一个节点的尴尬(乾坤大挪移)。
这里写图片描述
代码:

void LinkListErase3(LinkNode** phead,LinkNode** pos){
    if(phead == NULL){
    //非法输入
        return;
    }
    if(*phead == NULL){
    //空链表
        return;
    }
    if(*pos != NULL && (*pos)->next != NULL){
        LinkNode* to_delete = (*pos)->next;
        (*pos)->data = (*pos)->next->data;
        (*pos)->next = (*pos)->next->next;
        to_delete->next = NULL;
        free(to_delete);
    }
    if((*pos)->next == NULL){
        return;
    }
    return;
}

3.在一个无头单链表的一个节点前插入一个节点(不能遍历链表)


分析:我们可以创建一个和要插入的节点一样的节点,插入到指定节点的后面,然后将要需要插入的值赋给指定节点。(乾坤大挪移)
这里写图片描述
代码:

void LinkListInsertSp(LinkNode** phead, LinkNode* pos, LinkNodeType value){
    if(phead == NULL || pos == NULL){
    //非法输入
        return;
    }
    if(*phead == NULL){
    //空链表
        return;
    }
    LinkNode* tmp = CreateNode(pos->data);
    tmp->next = pos->next;
    pos->next = tmp;
    pos->data = value;
    return;
}

4.单链表实现约瑟夫环


分析:每次数到指定数目,开始从下一个元素重新计数。
这里写图片描述

代码:

LinkNode* JoseCycle(LinkNode* phead,size_t food){
    if(phead == NULL){
        return NULL;
    }
    if(food == 0){
        return NULL;
    }
    LinkNode* cur = phead;
    //当最终只剩一个元素的时候,cur == cur->next,停止循环。
    while(cur != cur->next){
        size_t i = 0;
        for(; i < (food - 1); ++i){
            cur = cur->next;
        }
        LinkNode* to_delete = cur->next;
        cur->data = cur->next->data;
        cur->next = cur->next->next;
        free(to_delete);
    }
    return cur;
}

5.逆置/反转单链表


分析:每次取下一个节点放到头结点的前面,然后将头指针前移。
这里写图片描述

代码:

void LinkListReverse(LinkNode** phead){
    if(phead == NULL){
    //非法输入
        return;
    }
    if(*phead == NULL){
    //空链表
        return;
    }
    if((*phead)->next == NULL){
    //只有一个节点
        return;
    }
    LinkNode* cur = *phead;
    while(cur->next != NULL){
        LinkNode* to_delete = cur->next;
        cur->next = to_delete->next;
        to_delete->next = *phead;
        *phead = to_delete;
    }
    return;
}

6.单链表冒泡排序


分析:没啥分析的,类似普通的冒泡排序
代码;

void Swap(LinkNodeType* a,LinkNodeType* b){
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}
void LinkListBubble(LinkNode* phead){
    if(phead == NULL){
        return;
    }
    LinkNode* count = phead;
    LinkNode* tail = NULL;
    for(;count != NULL;count = count->next){
        LinkNode* cur = phead;
        for(; cur->next != tail; cur = cur->next){
            if(cur->data > cur->next->data){
                Swap(&(cur->data) , &(cur->next->data));
            }
        }
        tail = cur;
    }
}

7.合并两个有序链表


分析:设置两个指针cur1,cur2 分别遍历链表,如果cur1的值小于cur2的值,因为链表十有序的,所以将cur1指向的内容存到新链表后,cur1 后移,同理,如果cur2的值小于cur1 的值,将cur2的值存到新链表后cur2后移。如果两指针指向的值相等,那么将两指针同时后移。最后如果有一个链表遍历结束,那么将另外一个链表剩下的部分加到新链表之后。
代码:

LinkNode* LinkListMerge(LinkNode* phead1,LinkNode* phead2){
    if(phead1 == NULL){
        return phead2;
    }
    if(phead2 == NULL){
        return phead1;
    }
    LinkNode* cur1 = phead1;
    LinkNode* cur2 = phead2;
    LinkNode* new_head = NULL;
    //设置tail指针是为了每次都能找到最后一个节点
    LinkNode* new_tail = NULL;
    while(cur1 != NULL && cur2 != NULL){
        if(cur1->data < cur2->data){
            if(new_head == NULL){
            //新链表里还没有元素
                new_head = new_tail = cur1;
            }
            else{
                new_tail->next = cur1;
                new_tail = new_tail->next;
            }
            cur1 = cur1->next;
        }
        if(cur1->data >= cur2->data){
            if(new_head == NULL){
                new_head = new_tail = cur2;
            }
            else{
                new_tail->next = cur2;
                new_tail = new_tail->next;
            }
            cur2 = cur2->next;
        }
    }

    if(cur1 != NULL){
    //cur2遍历结束,而cur1还未遍历完,将cur1接入新链表。
        new_tail->next = cur1;
    }

    if(cur2 != NULL){
        new_tail->next = cur2;
    }

    return new_head;
}

8.找到链表的中间节点


分析:设置两个指针,分别是fast.slow。fast每次后移两个节点,slow每次后移一个节点,所以当fast遍历到最后一个节点,slow正好指向链表的中间节点。
这里写图片描述

代码:

LinkNode* LinkListFindMid(LinkNode* phead){
    if(phead == NULL){
        return NULL;
    }
    LinkNode* fast = phead;
    LinkNode* slow = phead;
    while(fast->next != NULL && fast->next->next != NULL){
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

9.查找单链表倒数第k个节点(要求只能遍历一遍链表)


分析:设置两个指针,分别为fast,slow ,先让fast走步,这样slow到fast的距离就为k,然后,fast与slow同时后移。直到fast指向空,此时slow指向的位置就是倒数第k个位置。
这里写图片描述
代码:

LinkNode* LinkListFindKth(LinkNode* phead,size_t k){
    LinkNode* fast = phead;
    LinkNode* slow = phead;
    size_t i = 0;
    for(;i < k && fast != NULL;i++){
        fast = fast->next;
    }
    if(i < k){
        return NULL;
    }
    while(fast != NULL){
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}

10.删除链表的倒数第k个节点


分析:根据上道题,只要找到倒数第k个节点,我们就有一万种方法删除掉这节点。
代码:

void LinkListEraseKth(LinkNode** phead,size_t k){
    if(phead == NULL){
        return;
    }
    if(*phead == NULL){
        return;
    }

    size_t size = LinkListSize(*phead);
//如果k大于size,超出了范围,无法删除
    if(k > size){
        printf("超出范围,无法删除!\n");
        return;
    }

    LinkNode* pos_k = LinkListFindKth(*phead,k);

    if(pos_k->next != NULL){
        LinkNode* tmp = pos_k->next;
        pos_k->data = pos_k->next->data;
        pos_k->next = tmp->next;
        DestroyNode(tmp);
    }
    else if(pos_k->next == NULL){
        LinkListPopBack(phead);
    }
    return;
}

11.判断单链表是否带环,带环返回相遇点的指针


分析:设置两个指针,fast,slow,每次fast指针向后移两个指针,slow向后位移一个指针,如果一个链表带环,fast和slow一定还会再次相遇。因此,若果两个指针再次相遇,我们认为链表带环,如果链表不带环,fast一定会走到NULL。
这里写图片描述
代码:

LinkNode* LinkListHasCycle(LinkNode* phead){
    if(phead == NULL){
    //空链表
        return NULL;
    }
    LinkNode* fast = phead;
    LinkNode* slow = phead;
    while(fast != NULL && fast->next != NULL){
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow){
        //快慢指针再次相遇,链表一定带环
            return fast;
        }
    }
    //函数走到这儿一定不带环
    return NULL;
}

12.求环的入口点


分析:假设两个快慢指针,slow、fast,每次fast走两步,slow走一步,如果链表带环,那么快慢指针会再次相遇,此时,slow走过得路程是 x + z , fast走过得路程是 x + z + 环长,又因为fast 走的路程是slow走过得路程的2倍,所以可得 x = y。
这里写图片描述

分析到这儿,我们就可以设置一个指针cur从头结点开始每次走一步,另一个指针meet_pos从相遇出开始走,每次走一步,那么,cur和meet_pos相遇的位置就是链表环的入口点。

代码:

LinkNode* LinkListCycleEntryPos(LinkNode* phead){
    if(phead == NULL){
    //空链表
        return NULL;
    }
    LinkNode* meet_pos = LinkListHasCycle(phead);

    //meet_pos = NULL说明链表无环
    if(meet_pos == NULL){
        return NULL;

    LinkNode* cur = phead;
    while(cur != meet_pos){
        cur = cur->next;
        meet_pos = meet_pos->next;
    }
    return cur;
}

13.若链表带环,求环的长度


分析:设置一个指针从相遇点开始遍历,再次走到相遇点走过的长度就是环的长度。

size_t LinkListCycleSize(LinkNode* phead){
    LinkNode* meet_pos = LinkListHasCycle(phead);
    if(meet_pos == NULL){
        return 0;
    }
    LinkNode* cur = meet_pos;
    size_t count = 1;
    for(;cur->next != meet_pos;cur = cur->next){
        ++count;
    }
    return count;
}

14.判断两个链表是否相交,并求出交点


分析:两个链表可能一个带环,一个不带环;两个都带环;两个都不带环。
一个带环,一个不带环:一定不相交

都不带环:求两个链表的长度,并求长度差offset,较长的链表先走长度差offset步,然后两个链表同时遍历,如果两个指针会相遇,那么相遇点就是两链表的交点。
这里写图片描述

都带环

  • 交点在环外
    交点求法类似都不带环的求法,求两个链表头结点到入口点的距离,并求距离差offset,较长的先走offset步,然后一起走,相遇点就是交点。
    这里写图片描述

  • 交点在环内
    返两个链表的任意一个入口点作为交点返回。
    这里写图片描述
    代码:

LinkNode* LinkListHasCyclePos(LinkNode* phead1,LinkNode* phead2){
    if(phead1 == NULL || phead2 == NULL){
        return NULL;
    }
    //入口点
    LinkNode* meet_pos1 = LinkListHasCycle(phead1);
    LinkNode* meet_pos2 = LinkListHasCycle(phead2);
    LinkNode* cur1 = phead1;
    LinkNode* cur2 = phead2;
    if(meet_pos1 == NULL && meet_pos2 == NULL ){
        size_t size1 = LinkListSize(phead1);
        size_t size2 = LinkListSize(phead2);
        size_t offset = 0;//距离差
        if(size1 > size2){
            offset = size1 - size2;
            size_t i = 0;
            for(;i < offset;++i){
                cur1 = cur1->next;
            }
            while(cur1 != NULL && cur2 != NULL){
                if(cur1 == cur2){
                    return cur1;
                }
                cur1 = cur1->next;
                cur2 = cur2->next;
            }
        }else if(size1 <= size2){
            offset = size2 - size1;
            size_t i = 0;
            for(;i < offset; ++i){
                cur2 = cur2->next;
            }
            while(cur1 != NULL && cur2 != NULL){
                cur1 = cur1->next;
                cur2 = cur2->next;
                if(cur1 == cur2){
                    return cur1;
                }
            }
        }
        return NULL;
    }

   else if(meet_pos1 != NULL && meet_pos2 != NULL){
        LinkNode* Entry1 = LinkListCycleEntryPos(phead1);
        LinkNode* Entry2 = LinkListCycleEntryPos(phead2);
        size_t offset = 0;
        if(Entry1 == Entry2){
            size_t size1 = 0;
            size_t size2 = 0;
            for(;cur1 != Entry1;cur1 = cur1->next){
                size1++;
            }
            for(;cur2 != Entry2;cur2 = cur2->next){
                size2++;
            }
            cur1 = phead1;
            cur2 = phead2;
            if(size1 >= size2){
                offset = size1 - size2;
                size_t i = 0;
                for(;i < offset;++i){
                    cur1 = cur1->next;
                }
                while(cur1 != Entry1 && cur2 != Entry2){
                    if(cur1->data == cur2->data){
                        return cur1;
                    }
                    cur1 = cur1->next;
                    cur2 = cur2->next;
                }
            }else if(size1 < size2){
                offset = size2 - size1;
                size_t i = 0;
                for(;i < offset;++i){
                    cur2 = cur2->next;
                }
                while(cur1 != Entry1 && cur2 != Entry2){
                    if(cur1->data == cur2->data){
                        return cur1;
                    }
                    cur1 = cur1->next;
                    cur2 = cur2->next;
                }
            }
        }
        if(Entry1 != Entry2){
            return Entry1;
        }
    }
    else{
        return NULL;
    }
    return NULL;
}

求两个已排序单链表中相同的数据


分析:设置两个指针cur1、cur2分别遍历链表,如果 cur1->data > cur2->data
将 cur2->data存到新的链表,并且cur2后移到下个节点,同理,
cur2->data > cur1->data 将 cur1->data存到新的链表,并且cur1后移到下个节点。
若 cur1->data == cur2->data, 将cur1->data和cur2->data 都存到新链表中,两指针同时后移。

LinkNode* UnionSet(LinkNode* phead1,LinkNode* phead2){
    if(phead1 == NULL || phead2 == NULL){
        return NULL;
    }
    LinkNode* cur1 = phead1;
    LinkNode* cur2 = phead2;
    LinkNode* new_head = NULL;
    LinkNode* new_tail = NULL;//定义一个尾指针,便于找到最后一个节点
    while(cur1 != NULL && cur2 != NULL){
        if(cur1->data > cur2->data){
            cur2 = cur2->next;
        }else if(cur1->data < cur2->data){
            cur1 = cur1->next;
        }else{
            LinkNode* new_node = CreateNode(cur1->data);
            if(new_node == NULL){
               return NULL; 
            }
            if(new_head == NULL){
                new_head = new_tail = new_node;
                cur1 = cur1->next;
                cur2 = cur2->next;
            }else{
                new_tail->next = new_node;
                new_tail = new_tail->next;
                cur1 = cur1->next;
                cur2 = cur2->next;
            }
        }
    }
    return new_head;
}

以上是我总结的一些例题,如果后面继续碰到其他的题目,我将继续补充。另外还有一些建议,在写完每个函数之后,我们最好可以测试一下,这样可以减少后期解决问题的工作量。

下面是我的测试函数

#include"linklist.h"
#include<stdio.h>
#include<stdlib.h>
#include<stddef.h>

void TestInit(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    printf("phead = %p\n",phead);
}


void TestPushBack(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPrintChar(phead," 尾部插入");
    LinkListPopBack(&phead);
    LinkListPrintChar(phead," 尾部删除 ");
}

void TestLinkListPushFront(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPrintChar(phead," 尾部插入 ");
    LinkListPushFront(&phead,'x');
    LinkListPrintChar(phead," 头部插入 ");
    LinkListPopFront(&phead);
    LinkListPrintChar(phead," 头部删除 ");
}

void TestLinkListFind(){
    TestType;
    LinkNode* phead;
    LinkNode* cur;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListPrintChar(phead," 插入五个元素 ");
    cur = LinkListFind(phead,'d');
    printf("要查找的元素位于[%p]\n",cur);
}

void TestLinkListInsert(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListInsert(&phead,phead,'x');
    LinkListPrintChar(phead," 对空链表插入 ");
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListPrintChar(phead," 尾部插入五个元素 ");
    LinkListInsert(&phead,NULL,'y');
    LinkListPrintChar(phead," pos = NULL ");
    LinkListInsert(&phead,phead,'z');
    LinkListPrintChar(phead," pos = phead ");
    LinkNode* pos = LinkListFind(phead,'c');
    LinkListInsert(&phead,pos,'m');
    LinkListPrintChar(phead," c位置插入m ");
}

void TestLinkListInsertAfter(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListInsertAfter(&phead,phead,'x');
    LinkListPrintChar(phead," 对空链表插入 ");
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListPrintChar(phead," 尾部插入五个元素 ");
    LinkListInsertAfter(&phead,NULL,'y');
    LinkListInsertAfter(&phead,phead,'z');
    LinkListPrintChar(phead," pos = phead ");
    LinkNode* pos = LinkListFind(phead,'c');
    LinkListInsertAfter(&phead,pos,'m');
    LinkListPrintChar(phead," c位置插入m ");
}

void TestLinkListErase1(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkNode* pos = LinkListFind(phead,'d');
    LinkListErase1(&phead,phead);
    LinkListPrintChar(phead," 删除头节点 ");
    LinkListErase1(&phead,pos);
    LinkListPrintChar(phead," 删除d元素 ");
}

void TestLinkListErase2(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPrintChar(phead," 尾插四个元素 ");
    LinkNode* posa = LinkListFind(phead,'a');
    LinkNode* posd = LinkListFind(phead,'d');
    LinkListErase2(&phead,&posa);
    LinkListPrintChar(phead," 删除a元素 ");
    LinkListErase2(&phead,&posd);
    LinkListPrintChar(phead," 删除d元素 ");
}

void TestLinkListRemove(){
    TestType;
    LinkNode* phead;
    int empty;
    size_t count;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPrintChar(phead," 尾插四个元素 ");
    empty = LinkListEmpty(phead);
    printf("empty = [%d]\n",empty);
    count = LinkListSize(phead);
    printf("数组长度为:%zu\n\n",count);
    LinkListRemove(&phead,'c');
    LinkListPrintChar(phead," 删除元素c ");
}

void TestLinkListRemoveAll(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'b');
    LinkListPrintChar(phead," 尾部插入元素 ");
    LinkListRemoveAll(&phead,'b');
    LinkListPrintChar(phead," 删除所有的b ");
}

void TestLinkListPrintReverse(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPrintChar(phead," 尾部插入元素 ");
    LinkListPrintReverse(phead);
    printf("\n");
}

void TestLinkListErase3(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkNode* pos = LinkListFind(phead,'a');
    LinkListErase3(&phead,&pos);
    LinkListPrintChar(phead,"删除元素a");
    LinkNode* pos2 = LinkListFind(phead,'d');
    LinkListErase3(&phead,&pos2);
    LinkListPrintChar(phead,"删除元素d");
}

void TestLinkListInsertSp(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkNode* pos = LinkListFind(phead,'a');
    LinkListInsertSp(&phead,pos,'e');
    LinkListPrintChar(phead,"a元素前插入e");
}

void TestJoseCycle(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListPushBack(&phead,'f');
    LinkListPushBack(&phead,'g');
    LinkListPushBack(&phead,'h');
    LinkNode* cur = phead;    
    while( cur->next != NULL ){
        cur = cur->next;
    }
    cur->next = phead;
    LinkNode* pos = JoseCycle(phead,5);
    printf("运行结果:%c\t预期结果:c\n",pos->data);
}

void TestLinkListReverse(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListReverse(&phead);
    LinkListPrintChar(phead,"逆置链表");
}

void TestLinkListBubble(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'f');
    LinkListPushBack(&phead,'a');
    LinkListBubble(phead);
    LinkListPrintChar(phead,"链表排序");
}

void TestLinkListMerge(){
    TestType;
    LinkNode* phead1;
    LinkNode* phead2;
    LinkListInit(&phead1);
    LinkListInit(&phead2);
    LinkListPushBack(&phead1,'b');
    LinkListPushBack(&phead1,'d');
    LinkListPushBack(&phead1,'f');
    LinkListPushBack(&phead1,'h');
    LinkListPushBack(&phead1,'j');
    LinkListPushBack(&phead1,'l');
    LinkListPushBack(&phead2,'a');
    LinkListPushBack(&phead2,'c');
    LinkListPushBack(&phead2,'e');
    LinkListPushBack(&phead2,'g');
    LinkListPushBack(&phead2,'i');
    LinkListPushBack(&phead2,'k');
    LinkListPrintChar(phead1,"链表一");
    LinkListPrintChar(phead2,"链表二");
    LinkNode* phead = LinkListMerge(phead1,phead2);
    LinkListPrintChar(phead,"合并后的链表");
}

void TestLinkListFindMid(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'m');
    LinkNode* pos = LinkListFindMid(phead);
    printf("expect:c  actual:%c\n",pos->data);
}

void TestLinkListFindKth(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'m');
    LinkNode* pos = LinkListFindKth(phead,4);
    printf("expect:e  actual:%c\n",pos->data);
}

void TestLinkListEraseKth(){
    TestType;
    LinkNode* phead;
    LinkListInit(&phead);
    LinkListPushBack(&phead,'a');
    LinkListPushBack(&phead,'b');
    LinkListPushBack(&phead,'c');
    LinkListPushBack(&phead,'d');
    LinkListPushBack(&phead,'e');
    LinkListPushBack(&phead,'f');
    LinkListPushBack(&phead,'g');
    LinkListPushBack(&phead,'h');
    LinkListPushBack(&phead,'i');
    LinkListPrintChar(phead,"初始链表");
    LinkListEraseKth(&phead,1);
    LinkListPrintChar(phead,"删除元素i");
    LinkListEraseKth(&phead,4);
    LinkListPrintChar(phead,"删除元素e");
    LinkListEraseKth(&phead,7);
    LinkListPrintChar(phead,"删除元素a");
    LinkListEraseKth(&phead,15);
}

void TestLinkListHasCycle(){
    TestType;
    LinkNode* phead1;
    LinkNode* phead2;
    LinkNode* a;
    LinkNode* b;
    LinkListInit(&phead1);
    LinkListInit(&phead2);
    LinkListPushBack(&phead1,'a');
    LinkListPushBack(&phead1,'b');
    LinkListPushBack(&phead1,'c');
    LinkListPushBack(&phead1,'d');
    LinkListPushBack(&phead1,'e');
    LinkListPushBack(&phead1,'f');
    LinkNode* cur = phead1;
    while(cur->next != NULL){
        cur = cur->next;
    }
    cur->next = phead1;
    LinkListPushBack(&phead2,'a');
    LinkListPushBack(&phead2,'b');
    LinkListPushBack(&phead2,'c');
    LinkListPushBack(&phead2,'d');
    LinkListPushBack(&phead2,'e');
    LinkListPushBack(&phead2,'f');
    a = LinkListHasCycle(phead1);
    b = LinkListHasCycle(phead2);
    printf("actual:%p\n\n",a);
    printf("actual:%p\n",b);
}

void TestLinkListCycleSize(){
    TestType;
    LinkNode* phead1;
    LinkNode* phead2;
    size_t size1 = 0;
    size_t size2 = 0;
    LinkListInit(&phead1);
    LinkListInit(&phead2);
    LinkListPushBack(&phead1,'a');
    LinkListPushBack(&phead1,'b');
    LinkListPushBack(&phead1,'c');
    LinkListPushBack(&phead1,'d');
    LinkListPushBack(&phead1,'e');
    LinkListPushBack(&phead1,'f');
    LinkNode* cur = phead1;
    while(cur->next != NULL){
        cur = cur->next;
    }
    LinkNode* pos_c =  LinkListFind(phead1,'c');
    cur->next = pos_c;
    LinkListPushBack(&phead2,'a');
    LinkListPushBack(&phead2,'b');
    LinkListPushBack(&phead2,'c');
    LinkListPushBack(&phead2,'d');
    LinkListPushBack(&phead2,'e');
    LinkListPushBack(&phead2,'f');
    size1 = LinkListCycleSize(phead1);
    size2 = LinkListCycleSize(phead2);
    printf("%zu\n",size1);
    printf("%zu\n",size2);
}

void TestLinkListCycleEntryPos(){
    TestType;
    LinkNode* phead1;
    LinkNode* phead2;
    LinkListInit(&phead1);
    LinkListInit(&phead2);
    LinkListPushBack(&phead1,'a');
    LinkListPushBack(&phead1,'b');
    LinkListPushBack(&phead1,'c');
    LinkListPushBack(&phead1,'d');
    LinkListPushBack(&phead1,'e');
    LinkListPushBack(&phead1,'f');
    LinkNode* cur = phead1;
    while(cur->next != NULL){
        cur = cur->next;
    }
    LinkNode* pos_c =  LinkListFind(phead1,'c');
    cur->next = pos_c;
    LinkNode* Entry = LinkListCycleEntryPos(phead1);
    printf("expect:%p actual:%p\n",pos_c,Entry);
}

void TestLinkListHasCyclePos(){
    TestType;
    LinkNode* phead1;
    LinkNode* phead2;
    LinkListInit(&phead1);
    LinkListInit(&phead2);
    LinkListPushBack(&phead1,'a');
    LinkListPushBack(&phead1,'b');
    LinkListPushBack(&phead1,'c');
    LinkListPushBack(&phead1,'d');
    LinkListPushBack(&phead1,'e');
    LinkListPushBack(&phead1,'f');
    LinkNode* cur1 = phead1;
    while(cur1->next != NULL){
        cur1 = cur1->next;
    }
    LinkNode* pos_c =  LinkListFind(phead1,'c');
    cur1->next = pos_c;
    LinkListPushBack(&phead2,'g');
    LinkListPushBack(&phead2,'h');
    LinkListPushBack(&phead2,'i');
    LinkNode* cur2 = phead2;
    while(cur2->next != NULL){
        cur2 = cur2->next;
    }
    LinkNode* pos_b = LinkListFind(phead1,'b');
    cur2->next = pos_b;
    LinkNode* pos_x = LinkListHasCyclePos(phead1,phead2);
    printf("\n           ******************* 交点在环外 ***********************\n");
    printf("expect:%p actual:%p\n",pos_b,pos_x);
    LinkNode* pos_d = LinkListFind(phead1,'d');
    cur2->next = pos_d;
    LinkNode* pos_y = LinkListHasCyclePos(phead1,phead2);
    LinkNode* Entry = LinkListCycleEntryPos(phead1);
    printf("\n           ******************* 交点在环内 ***********************\n");
    printf("expect:%p actual:%p\n",Entry,pos_y);
    cur1->next = NULL;
    cur2->next = NULL;
    LinkNode* pos_z = LinkListHasCyclePos(phead1,phead2);
    printf("\n           ******************* 两个无环交点不相交 ***********************\n");
    printf("expect:NULL actual:%p\n",pos_z);
    LinkNode* pos_e = LinkListFind(phead1,'e');
    cur2->next = pos_e;
    LinkNode* pos_m = LinkListHasCyclePos(phead1,phead2);
    printf("\n           ******************* 两个无环交点相交 ***********************\n");
    printf("expect:%p actual:%p\n",pos_e,pos_m);
}

void TestUnionSet(){
    TestType;
    LinkNode* phead1;
    LinkNode* phead2;
    LinkListInit(&phead1);
    LinkListInit(&phead2);
    LinkListPushBack(&phead1,'a');
    LinkListPushBack(&phead1,'b');
    LinkListPushBack(&phead1,'c');
    LinkListPushBack(&phead1,'d');
    LinkListPushBack(&phead1,'e');
    LinkListPushBack(&phead1,'f');
    LinkListPushBack(&phead2,'d');
    LinkListPushBack(&phead2,'e');
    LinkListPushBack(&phead2,'f');
    LinkListPushBack(&phead2,'g');
    LinkListPushBack(&phead2,'h');
    LinkNode* new_head = UnionSet(phead1,phead2);
    LinkListPrintChar(new_head,"查找相同数据");
}

int main(){
    TestInit();
    TestPushBack();
    TestLinkListPushFront();
    TestLinkListFind();
    TestLinkListInsert();
    TestLinkListInsertAfter();
    TestLinkListErase1();
    TestLinkListErase2();
    TestLinkListRemove();
    TestLinkListRemoveAll();
    TestLinkListPrintReverse();
    TestLinkListErase3();
    TestLinkListInsertSp();
    TestJoseCycle();
    TestLinkListReverse();
    TestLinkListBubble();
    TestLinkListMerge();
    TestLinkListFindMid();
    TestLinkListFindKth();
    TestLinkListEraseKth();
    TestLinkListHasCycle();
    TestLinkListCycleSize();
    TestLinkListCycleEntryPos();
    TestLinkListHasCyclePos();
    TestUnionSet();
    printf("\n\n\n");
    return 0;
}

另外,头文件和Makefile奉上。

//linklist.h
#pragma once

#include<stddef.h>

#define TestType printf("\n*************************%s*****************************\n",__FUNCTION__);

typedef char LinkNodeType;

typedef struct LinkNode{
    LinkNodeType data;
    struct LinkNode* next;
}LinkNode;
void LinkListPrintReverse(LinkNode* phead);

void LinkListErase3(LinkNode** phead,LinkNode** pos);

void LinkListInsertSp(LinkNode** phead, LinkNode* pos, LinkNodeType value);

LinkNode* JoseCycle(LinkNode* phead,size_t food);

void LinkListReverse(LinkNode** phead);

void LinkListBubble(LinkNode* phead);

LinkNode* LinkListMerge(LinkNode* phead1,LinkNode* phead2);

LinkNode* LinkListFindMid(LinkNode* phead);

LinkNode* LinkListFindKth(LinkNode* phead,size_t k);

LinkNode* LinkListHasCycle(LinkNode* phead);

size_t LinkListCycleSize(LinkNode* phead);

LinkNode* LinkListCycleEntryPos(LinkNode* phead);

LinkNode* LinkListHasCyclePos(LinkNode* phead1,LinkNode* phead2);

LinkNode* LinkListHasCyclePos(LinkNode* phead1,LinkNode* phead2);

LinkNode* UnionSet(LinkNode* phead1,LinkNode* phead2);


//Makefile
linklist:linklist.c main.c 
    gcc $^ -o $@ -g
.PHONY:clean
clean:
    rm linklist

由于个人能力有限,以上的说明可能有Bug或者有什么不妥之处,欢迎发邮件到我的邮箱( Cyrus_wen@163.com )批评指正!

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: "数据结构经典例题名家代码"是一本经典的数据结构教材,其中包含了许多著名数据结构例题和相关的代码实现。 书中介绍了各种常见的数据结构,如线性结构(数组、链表、栈、队列)、树结构(二叉树、堆、哈夫曼树)、图结构(邻接矩阵、邻接表)等。每个数据结构都有详细的介绍和示例代码,可以帮助读者理解和掌握这些数据结构的基本原理和操作。 除了介绍数据结构的基本知识之外,书中还包含了许多算法和问题的实际应用。比如,如何使用数据结构解决迷宫问题、查找算法、排序算法等。这些例题和代码展示了数据结构在实际问题中的应用和灵活性,有助于读者培养解决问题的思维方式和能力。 作者通过清晰的讲解和简洁的代码示例,使得读者可以更好地理解和掌握数据结构的基本原理和使用方法。书中还附带了一些习题和答案,供读者巩固知识和提升编程能力。 综上所述,"数据结构经典例题名家代码"是一本非常适合学习和了解数据结构的经典教材,它通过丰富的例题和相关代码,帮助读者深入理解和掌握数据结构的概念和应用。无论是初学者还是有一定基础的人员,都可以从中受益。 ### 回答2: 经典例题名家代码之一是二叉树的遍历问题。二叉树是一种重要的数据结构,其遍历方式有三种:前序遍历、中序遍历和后序遍历。 前序遍历就是按照“根左右”的顺序访问二叉树的节点。中序遍历按照“左根右”的顺序进行访问,而后序遍历则是按照“左右根”的顺序进行访问。 以前序遍历为例,我们可以使用递归方法来实现。首先遍历根节点,然后递归地遍历左子树,最后递归地遍历右子树。这种递归思想可以通过以下代码实现: ```python class Node: def __init__(self, data): self.data = data self.left = None self.right = None def preorder_traversal(root): if root: print(root.data) preorder_traversal(root.left) preorder_traversal(root.right) # 示例二叉树 # 1 # / \ # 2 3 # / \ \ # 4 5 6 root = Node(1) root.left = Node(2) root.right = Node(3) root.left.left = Node(4) root.left.right = Node(5) root.right.right = Node(6) preorder_traversal(root) # 输出: 1 2 4 5 3 6 ``` 以上代码通过定义一个`Node`类来构造二叉树,并使用递归函数`preorder_traversal()`来进行前序遍历。输出结果为`1 2 4 5 3 6`,表示了二叉树的先序遍历结果。 通过这个经典的例题和代码,我们可以理解并掌握二叉树的遍历方式,深入理解数据结构的基本原理和常见操作。这些都是学习和理解数据结构的重要一步。 ### 回答3: 经典例题名家代码中,常见的一个例子是快速排序算法。快速排序算法是一种高效的排序算法,其核心思想是通过将待排序的序列划分为较小和较大的两个子序列,然后递归地对这两个子序列进行排序,最终实现整个序列的有序。 快速排序算法的名家代码主要体现在该算法的划分过程和递归思想上。其中,划分过程的关键是选择一个基准元素,并通过交换元素的位置,将小于基准元素的元素移到基准元素的左边,将大于基准元素的元素移到基准元素的右边。而递归思想则是对划分后的子序列进行递归调用,直到子序列只有一个元素或为空。 以下是一种经典的快速排序算法的代码示例: ``` void quickSort(int[] arr, int left, int right) { if (left < right) { int pivotIndex = partition(arr, left, right); // 获取基准元素的位置 quickSort(arr, left, pivotIndex - 1); // 对基准元素左边的子序列进行排序 quickSort(arr, pivotIndex + 1, right); // 对基准元素右边的子序列进行排序 } } int partition(int[] arr, int left, int right) { int pivot = arr[left]; // 选择第一个元素作为基准元素 int i = left, j = right; while (i < j) { while (i < j && arr[j] >= pivot) { j--; } if (i < j) { arr[i++] = arr[j]; } while (i < j && arr[i] <= pivot) { i++; } if (i < j) { arr[j--] = arr[i]; } } arr[i] = pivot; return i; } ``` 以上是一个典型的快速排序算法的代码实现,通过划分和递归的方式,能够高效地对一个序列进行排序。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值