单链表知识汇总

1.从尾到头打印单链表

void RPrintList(pList plist);//从尾到头打印链表(不改变链表)
 
 
  • 1
  • 1

从尾到头打印单链表,而不能改变链表本身,可以用递归很好的实现

void RPrintList(pList plist)
{
    if (plist == NULL)
    {
        return;
    }
    else if (plist->next == NULL)
    {
        printf("%d-->", plist->data);
    }
    else
    {
        RPrintList(plist->next);
        printf("%d-->", plist->data);
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

具体测试

void test1()
{
    pList plist = NULL;
    InitLinkList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PushFront(&plist, 5);
    PrintList(plist);
    RPrintList(plist);
}
int main()
{
    test1();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这里写图片描述

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

void EraseNotTail(pNode pos);//删除一个无头单链表的非尾节点
 
 
  • 1
  • 1

首先我们可以先把要删除节点的下一个节点保存下来,然后将该节点和它下一个节点的值进行交换,最后删除掉那个保存的下个节点

void EraseNotTail(pNode pos)
{
    pNode del = NULL;
    assert(pos->next);
    del = pos->next;
    pos->data = del->data;
    pos->next = del->next;
    free(del);
    del = NULL;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

具体测试:

void test2()
{
    pList plist = NULL;
    pNode pos = NULL;
    InitLinkList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PushFront(&plist, 5);
    PrintList(plist);
    pos = Find(plist, 2);
    EraseNotTail(pos);
    PrintList(plist);
}
int main()
{
    test2();
    system("pause");
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这里写图片描述

3. 在无头单链表的一个非头节点前插入一个节点

void InsertFrontNode(pNode pos, DataType d);//在无头单链表的一个非头结点前插入一个节点
 
 
  • 1
  • 1

同上一题类似,我们可以在指定位置的后面插入一个新节点,然后交换新节点和指定节点的数据即可

void InsertFrontNode(pNode pos, DataType d)
{
    pNode newNode = BuyNode(d);
    DataType tmp = 0;
    newNode->next = pos->next;
    pos->next = newNode;
    //交换数据
    tmp = pos->data;
    pos->data = newNode->data;
    newNode->data = tmp;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

具体测试:

void test3()
{
    pList plist = NULL;
    pNode pos = NULL;
    InitLinkList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PushFront(&plist, 5);
    PrintList(plist);
    pos = Find(plist, 2);
    InsertFrontNode(pos, 6);
    PrintList(plist);
}
int main()
{
    test3();
    system("pause");
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这里写图片描述

4. 单链表实现约瑟夫环

void JosephCycle(pList plist, int k);//单链表实现约瑟夫环
 
 
  • 1
  • 1

约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

void JosephCycle(pList plist, int k)
{
    pNode cur = plist;
    pNode del = NULL;
    int num = 0;
    while (1)
    {
        num = k;
        //当链表只剩下一个元素时停止循环
        if (cur == cur->next)
        {
            break;
        }
        while (--num)
        {
            cur = cur->next;
        }
        printf("%d ", cur->data);//打印删除的元素
        //进行交换删除(将要删除节点的data和它下一个节点的data进行交换,删除下一个节点)
        del = cur->next;
        cur->data = del->data;
        cur->next = del->next;
        free(del);
    }
    printf("\n剩下人的编号:%d\n", cur->data);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

具体测试:(在这里我们就用约瑟夫的小故事,给上41个人,每次报数报到3的人出列)

void test4()
{
    pList plist = NULL;
    int i = 0;
    InitLinkList(&plist);
    for (i = 41; i >= 1; i--)
    {
        PushFront(&plist, i);
    }
    Find(plist, 41)->next = plist;
    JosephCycle(plist, 3);
}
int main()
{
    test4();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这里写图片描述

5. 逆置/反转单链表

void ReverseList(pList *pplist);//逆序链表
 
 
  • 1
  • 1

与第一题从尾到头打印单链表不同的是,逆序链表我们要改变的是链表本身。可以先将链表的第一个节点取下来,保存在一个newHead中,随后依次把剩下的节点取下来连在第一个节点的前面。最后将newHead赋给管理之前链表得到指针pplist

void ReverseList(pList *pplist)
{
    pNode cur = *pplist;
    pNode tmp = NULL;
    pNode newHead = NULL;
    assert(pplist);
    //链表为空或者链表只有一个节点
    if ((*pplist == NULL) || (cur->next == NULL))
    {
        return;
    }
    while (cur)
    {
        tmp = cur;
        cur = cur->next;
        tmp->next = newHead;
        newHead = tmp;
    }
    *pplist = newHead;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

逆序链表的测试我们在下一个题中一起展示

6. 单链表排序

void BubbleSort(pList *pplist);//排序链表
 
 
  • 1
  • 1

因为现在暂时还没有接触快速排序,所以在这里只实现冒泡法排序链表

void BubbleSort(pList *pplist)
{
    pNode cur = *pplist;
    pNode tail = NULL;
    assert(pplist);
    //当链表为空或者只有一个节点时
    if ((cur == NULL) || (cur->next == NULL))
    {
        return;
    }
    //总趟数
    while (cur->next != tail)
    {
        //一趟排序
        while (cur->next != tail)
        {
            if ((cur->data) > (cur->next->data))
            {
                DataType tmp = cur->data;
                cur->data = cur->next->data;
                cur->next->data = tmp;
            }
            cur = cur->next;
        }
        tail = cur;
        cur = *pplist;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

具体测试

void test6()
{
    pList plist = NULL;
    pNode pos = NULL;
    InitLinkList(&plist);
    PushFront(&plist, 7);
    PushFront(&plist, 2);
    PushFront(&plist, 4);
    PushFront(&plist, 3);
    PushFront(&plist, 5);
    PushFront(&plist, 1);
    PushFront(&plist, 6);
    printf("正常:");
    PrintList(plist);
    ReverseList(&plist);
    printf("逆序后:");
    PrintList(plist);
    BubbleSort(&plist);
    printf("排序后:");
    PrintList(plist);
}
int main()
{
    test6();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

这里写图片描述

7.合并两个有序链表,合并后依然有序

pList Merge(pList *pplist1, pList *pplist2);//合并两个有序单链表,合并后依然有序
pList DMerge(pList *pplist1, pList *pplist2);//递归实现两条链表合并
 
 
  • 1
  • 2
  • 1
  • 2

在这里我们分别用递归和非递归进行实现,直接上代码:

pList Merge(pList *pplist1, pList *pplist2)
{
    pNode cur1 = *pplist1;
    pNode cur2 = *pplist2;
    pList newHead = NULL;
    pNode tail = NULL;
    assert(pplist1);
    assert(pplist2);
    if ((*pplist1 == NULL) && (*pplist2 == NULL))
    {
        return NULL;
    }
    if (*pplist1 == *pplist2)
    {
        return *pplist1;
    }
    if (*pplist1 == NULL)
    {
        return *pplist2;
    }
    if (*pplist2 == NULL)
    {
        return *pplist1;
    }
    if ((cur1->data) > (cur2->data))
    {
        newHead = cur2;
        cur2 = cur2->next;
    }
    else
    {
        newHead = cur1;
        cur1 = cur1->next;
    }
    newHead->next = NULL;
    tail = newHead;
    while (cur1&&cur2)
    {
        if ((cur1->data) > (cur2->data))
        {
            tail->next = cur2;
            cur2 = cur2->next;
        }
        else
        {
            tail->next = cur1;
            cur1 = cur1->next;
        }
        tail = tail->next;
        tail->next = NULL;
    }
    if (cur1 == NULL)
    {
        tail->next = cur2;
    }
    else if (cur2 == NULL)
    {
        tail->next = cur1;
    }
    return newHead;
}
pList DMerge(pList *pplist1, pList *pplist2)//递归实现两条链表合并
{
    pNode cur1 = *pplist1;
    pNode cur2 = *pplist2;
    pList newHead = NULL;
    pNode tail = NULL;
    assert(pplist1);
    assert(pplist2);
    if ((cur1 == NULL) && (cur2 == NULL))
    {
        return NULL;
    }
    if (cur1 == cur2)
    {
        return cur1;
    }
    if (cur1 == NULL)
    {
        return cur2;
    }
    if (cur2 == NULL)
    {
        return cur1;
    }
    if ((cur1->data) > (cur2->data))
    {
        newHead = cur2;
        cur2 = cur2->next;
    }
    else
    {
        newHead = cur1;
        cur1 = cur1->next;
    }
    newHead->next = NULL;
    if (cur1 == NULL || cur2 == NULL);
    {
        tail = newHead;
        tail->next = DMerge(&cur1, &cur2);
    }
    return newHead;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

具体测试:

void test7()
{
    pList plist1 = NULL;
    InitLinkList(&plist1);
    PushFront(&plist1, 9);
    PushFront(&plist1, 7);
    PushFront(&plist1, 5);
    PushFront(&plist1, 3);
    PushFront(&plist1, 1);

    pList plist2 = NULL;
    InitLinkList(&plist2);
    PushFront(&plist2, 10);
    PushFront(&plist2, 8);
    PushFront(&plist2, 6);
    PushFront(&plist2, 4);
    PushFront(&plist2, 2);
    PushFront(&plist2, 0);

    pList newList1 = NULL;
    newList1 = Merge(&plist1, &plist2);
    PrintList(newList1);
    pList newList2 = NULL;
    newList2 = DMerge(&plist1, &plist2);
    PrintList(newList2);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

这里写图片描述

8. 查找单链表的中间节点,要求只能遍历一次链表

pNode FindMidNode(pList plist);//查找链表的中间节点(只能遍历一次链表)
 
 
  • 1
  • 1

查找链表中间节点,我们可以定义两个指针,快指针每次次走两步,慢指针每次走一步,当快指针到达链表尾部时,慢指针所在的位置就是中间节点的位置。

pNode FindMidNode(pList plist)
{
    pNode fast = plist;
    pNode slow = plist;
    if (plist == NULL)
    {
        return NULL;
    }
    while (fast&&fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

具体测试:

void test8()
{
    pNode pos = NULL;
    pList plist1 = NULL;
    InitLinkList(&plist1);
    PushFront(&plist1, 9);
    PushFront(&plist1, 7);
    PushFront(&plist1, 5);
    PushFront(&plist1, 3);
    PushFront(&plist1, 1);

    pList plist2 = NULL;
    InitLinkList(&plist2);
    PushFront(&plist2, 10);
    PushFront(&plist2, 8);
    PushFront(&plist2, 6);
    PushFront(&plist2, 4);
    PushFront(&plist2, 2);
    PushFront(&plist2, 0);

    pList newList1 = NULL;
    newList1 = Merge(&plist1, &plist2);
    PrintList(newList1);
    pos = FindMidNode(newList1);
    printf("中间节点:%d\n", pos->data);
}
int main()
{
    test8();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

这里写图片描述

9. 删除链表的倒数第K个结点

void DelKNode(pList plist, int k);//删除链表的倒数第K个节点
 
 
  • 1
  • 1

同样的,定义两个指针。一个指针first先走上K 步,然后第二个指针second再和first指针同时走,当first指针到达链表尾部时,second指针所在的位置就为倒数第K个节点的位置,随后我们用之前讲过交换数据的方法删除即可 
(注意,因为是用交换删除的方法,如果K=1,则它没有下一个节点。我们可以对这种情况进行单独处理,这时候函数就和尾删一样了,我在这里直接调用尾删的接口)

void DelKNode(pList plist, int k)
{
    pNode first = plist;
    pNode second = plist;
    if (k == 1)
    {
        PopBack(&plist);
        return;
    }
    while (first&&first->next)
    {
        //找到倒数第K个节点
        first = first->next;
        if (--k <= 0)
        {
            second = second->next;
        }
    }
    //交换删除倒数第K个
    if (k <= 0)
    {
        pNode del = second->next;
        second->data = del->data;
        second->next = del->next;
        free(del);
        del = NULL;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

具体测试:

void test9()
{
    pList plist = NULL;
    InitLinkList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PushFront(&plist, 5);
    PushFront(&plist, 6);
    PushFront(&plist, 7);
    PrintList(plist);
    DelKNode(plist, 1);
    PrintList(plist);
    DelKNode(plist, 3);
    PrintList(plist);
}
int main()
{
    test9();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

这里写图片描述

10. 判断单链表是否带环?若带环,求环的长度?求环的入口点?

pNode CheckCycle(pList plist);//判断链表是否带环
int GetCircleLength(pNode meet);//求环的长度
pNode GetCircleEntryNode(pNode meet, pList plist);//求环的入口点
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

(1)判断链表是否带环 
还是定义上两个指针,快指针每次走两步,慢指针每次走一步,如果链表带环的话,两个指针必定会相遇,而且相遇点一定在环上。

pNode CheckCycle(pList plist)//判断链表是否带环
{
    pNode fast = plist;
    pNode slow = plist;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)//快指针和慢指针一定会在环上相遇
        {
            return slow;//相遇点
        }
    }
    return NULL;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

(2)求环的长度 
定义一个指针,从相遇点(见链表判环问题)开始走,在定义一个计数器,每走一步计数器家家,当下次到达相遇点时计数器的大小就是环的长度了。

int GetCircleLength(pNode meet)//求环的长度
{
    int count = 0;
    pNode cur = meet;
    do
    {
        cur = cur->next;
        count++;
    } while (cur != meet);
    return count;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(3)求环的入口点 
在这里我们介绍两种方法 
方法一:从开始到环入口点的距离x = 环长度len的整数倍c*len - 从环入口点到相遇点的距离y 
定义一个指针从开始出发,另一个指针从相遇点出发,两个指针再次相遇的点就为入口点 
详见下图: 
这里写图片描述

方法二:定义两个指针,一个先走环的长度步,另外一个在开始走,两个指针相遇的地方就为环的入口点

pNode GetCircleEntryNode(pNode meet, pList plist)//求环的入口点
{
    //方法一:从开始到环入口点的距离x = 环长度len的整数倍c*len - 从环入口点到相遇点的距离y
    //定义一个指针从开始出发,另一个指针从相遇点出发,两个指针再次相遇的点就为入口点
    //pNode cur = plist;
    //while (cur != meet)
    //{
    //  cur = cur->next;
    //  meet = meet->next;
    //}
    //return cur;
    //方法二:定义两个指针,一个先走环的长度步,另外一个在开始走,两个指针相遇的地方就为环的入口点
    pNode first = plist;
    pNode second = plist;
    int num = GetCircleLength(meet);
    while (num--)
    {
        first = first->next;
    }
    while (first != second)
    {
        first = first->next;
        second = second->next;
    }
    return first;

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

具体测试:

void test10()
{
    pList plist = NULL;
    pNode pos = NULL;
    pNode entry = NULL;
    InitLinkList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PushFront(&plist, 5);
    PushFront(&plist, 6);
    PushFront(&plist, 7);
    PrintList(plist);
    DelKNode(plist, 1);
    PrintList(plist);
    DelKNode(plist, 3);
    PrintList(plist);
    Find(plist, 1)->next = Find(plist, 4);
    pos = CheckCycle(plist);
    if (pos == NULL)
    {
        printf("不带环\n");
    }
    else
    {
        printf("带环\n");
        printf("相遇点:%d\n", pos->data);
    }
    printf("环长度:%d\n", GetCircleLength(pos));
    entry = GetCircleEntryNode(pos, plist);
    printf("入口点:%d\n", entry->data);
}
int main()
{
    test10();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

这里写图片描述

11. 判断两个链表是否相交,若相交,求交点。(假设链表不带环)

pNode CheckCross(pList p1, pList p2);//判断两条链表是否相交(两条链表都不带环)
 
 
  • 1
  • 1

在这里我们只讨论两条链表都不带环的情况 
对于求交点的问题我们也给出两种实现方法: 
方法一:将两条相交的链表连成一个带环的链表,将第一个链表尾部的next指向第二个链表的头部再利用之前实现过的接口,把问题转化成求环的入口点 
方法二:第一条链表的长度为len1,第二条链表的长度为len2,定义一个指针从长度长的链表先走|len1-len2|步,第二个指针再从长度短的链表走,当两个指针第一次指向地址相同的那个节点,则该节点为相交节点。

pNode CheckCross(pList p1, pList p2)//判断两条链表是否相交(两条链表都不带环)
{
    pNode cur1 = p1;
    pNode cur2 = p2;
    int len1 = 0;
    int len2 = 0;
    int num = 0;
    //两条链表有任意一条为空则不相交
    if ((cur1 == NULL) || (cur2 == NULL))
    {
        return NULL;
    }
    while (cur1->next)
    {
        cur1 = cur1->next;
        len1++;
    }
    while (cur2->next)
    {
        cur2 = cur2->next;
        len2++;
    }
    //相交返回交点
    if (cur1 == cur2)
    {
        //方法一:将两条相交的链表连成一个带环的链表,将第一个链表尾部的next指向第二个链表的头部
        //再利用之前实现过的接口,把问题转化成求环的入口点
        //cur1->next = p2;
        //pNode meet = CheckCycle(p1);
        //pNode cross = GetCircleEntryNode(meet, p1);
        //return cross;
        //方法二:第一条链表的长度为len1,第二条链表的长度为len2,定义一个指针从长度长的链表先走|len1-len2|步,
        //第二个指针再从长度短的链表走,当两个指针第一次指向地址相同的那个节点,则该节点为相交节点。
        num = abs(len1 - len2);//求绝对值
        if (len1 > len2)
        {
            cur1 = p1;
            cur2 = p2;
        }
        else
        {
            cur2 = p1;
            cur1 = p2;
        }
        for (int i = 0; i < num; i++)
        {
            cur1 = cur1->next;
        }
        while (cur1 != cur2)
        {
            cur1 = cur1->next;
            cur2 = cur2->next;
        }
        return cur1;
    }
    else
    {
        return NULL;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

具体测试:

void test11()
{
    pList plist1 = NULL;
    pNode cross = NULL;
    InitLinkList(&plist1);
    PushFront(&plist1, 10);
    PushFront(&plist1, 9);
    PushFront(&plist1, 7);
    PushFront(&plist1, 5);
    PushFront(&plist1, 3);
    PushFront(&plist1, 1);

    pList plist2 = NULL;
    InitLinkList(&plist2);
    PushFront(&plist2, 6);
    PushFront(&plist2, 4);
    PushFront(&plist2, 2);
    PushFront(&plist2, 0);
    Find(plist2, 6)->next = Find(plist1, 5);
    cross = CheckCross(plist1, plist2);
    printf("相交点:%d\n", cross->data);
}
int main()
{
    test11();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

这里写图片描述

12. 复杂链表的复制。

pComplexNode BuyComplexNode(DataType d);//创建复杂链表的节点
void PrintComplexList(pComplexNode p);//打印复杂链表
pComplexNode CopyComplexList(pComplexNode head);//复制复杂链表

 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

复杂链表:一个链表的每个节点,有一个指向next指针指向下一个节点,还有一 个random指针指向这个链表中的一个随机节点或者NULL,现在要求实现复制这个链表, 返回复制后的新链表 
先来看看复杂链表的结构:

//复杂链表
typedef struct ComplexNode
{
    DataType *data;
    struct ComplexNode *next;
    struct ComplexNode *random;
}ComplexNode, *pComplexNode;

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我们可以把复杂链表的复制分为三个部分 
(1)、复制复杂链表的每个节点,并插入到该节点的后面 
(2)、调整复制节点的random指针指向 
(3)、分离两条链表 
贴一张我在实现过程中画的草图,希望能够帮助大家更好的理解(草图!草图!草图!) 
紫色区域为random指针域,绿色区域为next指针域 
我们可以看到,复制节点的random指向刚好就是指向它那个节点random指针的next的指向。 
这里写图片描述

pComplexNode BuyComplexNode(DataType d)//创建复杂链表的节点
{
    pComplexNode newNode = (pComplexNode)malloc(sizeof(ComplexNode));
    if (newNode == NULL)
    {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    newNode->data = d;
    newNode->next = NULL;
    newNode->random = NULL;
    return newNode;
}
void PrintComplexList(pComplexNode p)//打印复杂链表
{
    pComplexNode cur = p;
    while (cur)
    {
        printf("[%d]-->(%d)-->", cur->data, cur->random->data);
        cur = cur->next;
    }
    printf("NULL\n");
}
pComplexNode CopyComplexList(pComplexNode head)//复制复杂链表
{
    pComplexNode pold = head;
    pComplexNode cur = NULL;
    pComplexNode newNode = NULL;
    pComplexNode newHead = NULL;

    //1.复制原来复杂链表的每个节点并插入到该节点后
    while (pold)
    {
        cur = pold->next;
        newNode = BuyComplexNode(pold->data);
        pold->next = newNode;
        newNode->next = cur;
        pold = cur;
    }
    //2.调整random指针的指向
    pold = head;
    while (pold)
    {
        newNode = pold->next;
        if (pold->random)
        {
            cur = pold->random->next;
            newNode->random = cur;
        }
        pold = newNode->next;
    }
    //3.分离两条链表
    pold = head;
    if (pold != NULL)
    {
        newHead = pold->next;
    }
    while (pold->next->next)
    {
        newNode = pold->next;
        pold->next = newNode->next;
        pold = newNode->next;
        newNode->next = pold->next;
    }
    pold->next = NULL;
    return newHead;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

具体测试:

void test12()
{
    pComplexNode newList = NULL;
    pComplexNode p1 = NULL;
    pComplexNode p2 = NULL;
    pComplexNode p3 = NULL;
    pComplexNode p4 = NULL;
    p1 = BuyComplexNode(1);
    p2 = BuyComplexNode(2);
    p3 = BuyComplexNode(3);
    p4 = BuyComplexNode(4);
    p1->next = p2;
    p2->next = p3;
    p3->next = p4;
    p1->random = p3;
    p2->random = p4;
    p3->random = p2;
    p4->random = p3;
    PrintComplexList(p1);
    newList = CopyComplexList(p1);
    PrintComplexList(newList);
}
int main()
{
    test12();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值