单向链表的基本操作及常见面试题

一、单链表的基本操作 
1、初始化链表 
2、尾部插入 
3、尾部删除 
4、头部插入 
5、头部删除 
6、销毁链表 
7.打印单链表内容 
8、查询数据为d的节点 
9、获取单链表最后一个节点 
10、在指定位置之前插入一个节点 
11、指定位置节点删除 
12、删除数据为d,第一次出现的节点 
13、删除数据为d的所有节点 
14、创建新节点
二,链表常见面试题 
1、删除指定位置的非尾节点 
2、获取链表长度
3、逆序打印单链表
4、在无头单链表的一个节点前插入一个节点
5、单链表实现约瑟夫环
6、逆置单链表
7、单链表排序
8、合并两个单链表
9、找单链表中间节点,只能遍历一次链表
10、找单链表的倒数第k个节点,只能遍历一次链表
11、判断单链表是否带环
12、求有环链表中环的长度
13、求带环单链表环的入口
14、判断两个不带环的单链表是否相交,若相交,返回交点,若不想交,返回NULL
15、判断两个带环的单链表是否相交,若相交,返回交点,若不想交,返回NULL 
16、复制复杂链表
17、求两个已排好序的单链表中的相同数据
18、删除倒数第k个节点

头文件 LinkList.h :

#ifndef __LINKLIST_H__
#define __LINKLIST_H__

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int DataType;

typedef struct NODE
{
    DataType _data;//数据域
    struct NODE *_pNext;//指针域,指向下一个节点
}Node, pList;

typedef struct CSLinkList //复杂链表,比正常链表多一个随机指针域,指向任意位置
{
    DataType _data;
    struct CSLinkList *_random;
    struct CSLinkList *_next;
}CSLinkList;

void InitLinkList(pList **pHead);//初始化链表
pList* BuyNode(DataType d)//创建新节点

void PushBack (pList **pHead, DataType d);//尾部插入

void PopBack (pList **pHead);//尾部删除

void PushFront (pList **pHead, DataType d);//头部插入

void PopFront (pList **pHead);//头部删除

void DestroyLInkLIst (pList **pHead);//销毁链表

void PrintLinkList (pList *pHead);//打印单链表内容

Node* FindNode (pList *pHead, DataType d);//查找数据为d节点

Node* LinkListBack(pList* pHead);// 获取单链表最后一个节点 

void Insert (pList **pHead, Node* pos, DataType d);//在指定位置之前插入一个值 

void Erase (pList **pHead, Node *pos);//指定位置删除

void Remove (pList **pHead, DataType d);//删除数据d第一次出现的节点

void RemoveAll (pList **pHead, DataType d);//删除出现指定元素的所有节点
void EraseNotTailNode (Node *pos);//删除指定位置的非尾节点

int GetLength (pList *pHead);//获取链表长度

void PrintLinkListFromTailToHead (pList *pHead);//逆序打印单链表

void InsertWithoutHead (Node *pos, DataType d);//在无头单链表的一个节点前插入一个节点

Node* JosephCircle (pList **pHead, int k);//单链表实现约瑟夫环

void Reverse(pList **pHead);//逆置单链表

void LinkListSort (pList **pHead);//单链表排序

Node *MergerLinkList (pList *pHead1, pList *pHead2);//合并两个单链表

Node* FindMiddleNode (pList *pHead);//找单链表中间节点,只能遍历一次链表

Node* FindLastKNode (pList *pHead, int k);//找单链表的倒数第k个节点,只能遍历一次链表

Node* LinkListHaveCircle(pList *pHead);//判断单链表是否带环

int LinkListWithCircleLen (pList *pHead);//求有环链表中环的长度

Node *CircleEntrance (pList *pHead);    //求带环单链表环的入口

Node *IsListintersect (pList *pHead1, pList* pHead2);//判断两个不带环的单链表是否相交,若相交,返回交点,若不想交,返回NULL

Node *IsListintersectWithCircle (pList *pHead1, pList* pHead2);//判断两个带环的单链表是否相交,若相交,返回交点,若不想交,返回NULL

CSLinkList *CopyComplexLinkList (CSLinkList *PHead);//复制复杂链表

void UnionSet(Node *li, Node *l2);//求两个已排好序的单链表中的相同数据

void DeleteLastKNode (pList **pHead, int k);//删除倒数第k个节点

#endif

实现过程(main.c文件):

一、链表基本操作:

1、链表初始化

void InitLinkList(pList **pHead)//初始化链表,数据域为0,指针域为空
{
    pList *cur = NULL;
    assert (pHead != NULL);//传来的地址不能为空

    cur =(pList *) malloc (sizeof (pList));

    if (NULL == cur)//开辟空间失败,程序结束
    {
        perror ("InitLinkList::malloc>>");
        exit (1);
    }
    (*pHead) = cur;
    (*pHead)->_data = 0;
    (*pHead)->_pNext = NULL;
}

2、创建新节点:

pList* BuyNode(DataType d)//创建新节点
{
    pList *NewNode = (pList *)malloc (sizeof (pList));
    if (NewNode == NULL)
    {
        perror ("BuyNode :: malloc>>");
        system ("pause");
        exit (1);
        return NULL;
    }
    NewNode->_data = d;
    NewNode->_pNext = NULL;
    return NewNode;
}

3、尾部插入

void PushBack(pList **pHead, DataType d)//尾部插入
{
    pList *cur = *pHead;
    assert (pHead != NULL);//传入的地址不为空
    //1.链表为空
    if (*pHead == NULL)
    {
        *pHead = BuyNode (d);
    }
    //2.链表不为空
    else 
    {
        while (cur->_pNext != NULL)
        {
            cur = cur->_pNext;//找最后一个节点
        }
        cur->_pNext = BuyNode(d);
    }
}

4、尾部删除

void PopBack (pList **pHead)//尾部删除
{
    if (*pHead == NULL)//链表为空
    {
        printf ("链表为空,无法删除!!\n");
        return;
    }
    else if ((*pHead)->_pNext == NULL)//链表只有一个节点
    {
        pList *cur = *pHead;
        free (cur);
        cur = NULL;
        (*pHead) = NULL;
        return;
    }
    else//链表有多个节点
    {
        pList *cur = *pHead;
        pList *pre = NULL;
        while (cur->_pNext)
        {
            pre = cur;
            cur = cur->_pNext;
        }
        pre ->_pNext = NULL;
        free (cur);
        cur = NULL;
    }
}

5、头部插入

void PushFront (pList **pHead, DataType d)//头部插入
{
    pList *cur = BuyNode (d);
    assert (pHead);

    cur ->_pNext = (*pHead);
    (*pHead) = cur;
}

6、头部删除

void PopFront (pList **pHead)//头部删除
{
    if (*pHead == NULL)//链表为空
    {
        printf ("链表为空,无法删除!!\n");
        return;
    }
    else
    {
        pList *cur = *pHead;
        *pHead = (*pHead)->_pNext;
        free (cur);
        cur = NULL;
    }

}

7、打印单链表内容

void PrintLinkList (pList *pHead)//打印单链表内容
{
    pList *cur = pHead;
    while (cur)
    {
        printf ("%d -> ", cur->_data);
        cur = cur ->_pNext;
    }
    printf ("NULL\n\n");
}

8、销毁单链表

void DestroyLInkLIst(pList **pHead)//销毁链表
{
    pList *cur = (*pHead);
    pList *pre = NULL;

    assert (pHead != NULL);//传来的地址不能为空

    while (NULL != cur)
    {
        pre = cur;
        cur = cur->_pNext;
        free (pre);
        pre = NULL;
    }
    (*pHead) = NULL;
    printf ("链表销毁成功!!\n");
}

9、查找数据为d节点,返回该节点地址;如果没有该节点返回空

Node* FindNode (pList *pHead, DataType d)//查找数据为d节点,返回该节点地址;如果没有该节点返回空
{
    pList *cur = pHead;
    if (NULL == pHead)//链表为空,直接返回NULL
    {
        return NULL;
    }

    while (cur)//寻找数据域为 d 的节点,找到了直接返回该节点
    {
        if (d == cur->_data)
        {
            return cur;
        }
        cur = cur->_pNext;
    }

    return NULL;//如果链表都遍历完都找不到返回NULL
}

10、获取单链表最后一个节点

Node* LinkListBack(pList* pHead)
{
    Node* pPreCur = NULL;
    Node* pCur = pHead;
    while(pCur)
    {
        pPreCur = pCur;
        pCur = pCur->_pNext;
    }

    return pPreCur;
}

11、 在指定位置之前插入一个值

void Insert (pList **pHead, Node* pos, DataType d)//在指定位置之前插入一个值
{
    pList *cur = *pHead;     //用来寻找指定位置
    pList *pre = NULL;       //用来标记指定位置的前一个位置
    pList *new_node = NULL;  //要插入的新节点

    assert (pHead != NULL && pos != NULL);

    if (*pHead == NULL)
    {
        printf ("链表为空,插入失败!!\n");
        return;
    }

    if  (pos == *pHead)   //如果给定插入位置为第一个节点
    {
        new_node = BuyNode (d);
        new_node ->_pNext = *pHead;
        *pHead = new_node;
    }
    else    //如果指定插入位置不是第一个节点
    {
        while (cur)
        {
            if (cur == pos)
            {
                new_node = BuyNode (d);
                pre ->_pNext = new_node;
                new_node ->_pNext = cur;
            }
            pre = cur;
            cur = cur->_pNext;
        }
    }
}

12、删除指定位置节点

void Erase (pList **pHead, Node *pos)//指定位置删除
{
    assert (pHead != NULL && pos != NULL);

    if (*pHead == NULL)
    {
        printf ("链表为空,无法删除!!!\n");
        return;
    }

    if (*pHead == pos)//如果要删除的节点为第一个节点
    {
        *pHead = (*pHead)->_pNext;
        free (pos);
        pos = NULL;
    }
    else
    {
        pList *cur = *pHead;
        pList *pre = NULL;

        while (cur)
        {
            if (pos == cur)
            {
                pre->_pNext = cur->_pNext;
                free (cur);
                cur = NULL;
                return;
            }

            pre = cur;
            cur = cur->_pNext;
        }
        printf ("没有找到该指定节点,删除失败!!!\n");

    }
}

13、删除数据d第一次出现的节点

void Remove (pList **pHead, DataType d)//删除数据d第一次出现的节点
{
    pList *cur = *pHead;
    pList *pre = NULL;

    assert (pHead != NULL);

    if (*pHead == NULL)
    {
        printf ("链表为空,无法删除!!!\n");
        return;
    }

    if ((*pHead)->_data == d)   //d第一次出现的节点是第一个节点
    {
        *pHead = (*pHead)->_pNext;
        free (cur);
        cur = NULL;
    }
    else    //第一次出现d的节点不是第一个节点
    {
        pre = cur;
        cur = cur->_pNext;

        while (cur)
        {
            if (d == cur->_data)
            {
                pre->_pNext = cur->_pNext;
                free (cur);
                cur = NULL;
                return;
            }

            pre = cur;
            cur = cur->_pNext;
        }
        printf ("链表中没有该元素!!!\n");
    }
}

14、删除所有指定元素出现的节点

void RemoveAll (pList **pHead, DataType d)//删除出现指定元素的所有节点
{
    pList *cur = *pHead;//依次遍历链表,并判断各个节点要不要删除
    pList *pre = NULL;//标记不要删除的节点
    pList *del = NULL;//标记要删除的节点

    assert (pHead != NULL);

    if (*pHead == NULL)
    {
        printf ("链表为空,无法删除!!!\n");
        return;
    }

    while (cur != NULL)
    {
        if ((*pHead)->_data == d)   //需要删除的节点是第一个节点,将第一个节点删除,使cur重新指向第一个节点
        {
            (*pHead) = (*pHead)->_pNext;
            free (cur);
            cur = NULL;
            cur = *pHead;
        }
        else    //要删除的节点不是第一个节点
        {
            if (cur->_data == d)    //如果这个节点要删除,用cur指向它的下一个节点,用del指向要删除的节点
            {
                del = cur;
                cur = cur->_pNext;
                pre ->_pNext = cur;
                free (del);
                del = NULL;
            }
            else    //如果这个节点不要删除,用cur指向它的下一个节点,用pre指向这一个节点
            {
                pre = cur;
                cur = cur->_pNext;
            }

        }
    }
}

二、常见面试题分析

1、从尾到头打印单链表

void PrintLinkListFromTailToHead (pList *pHead)//逆序打印单链表
{
    if (pHead != NULL)
    {
        PrintLinkListFromTailToHead (pHead->_pNext);
        printf ("%d -> ", pHead->_data);
    }
}

2、获取链表长度

int GetLength (pList *pHead)
{
   int count = 0;
   pList *p = pHead ;
   if(pHead == NULL)
   {
     printf("该链表为空链表\n");
     return 0;
   }
   while(p != NULL )
   {
       count++;
       p = p ->_pNext;      
   }
    return count;
}

3、删除一个无头单链表的非尾节点(要求不能遍历链表)

思路:我们可以把要删除的节点数据改为它下一个节点中的数据,然后删除它的下一个节点。

void EraseNotTailNode (Node *pos)//删除指定位置的非尾节点
{
    Node *cur = pos->_pNext;
    assert (pos != NULL);

    //将pos的下个节点的值赋给pos,在删除pos的下一个节点
    pos->_data = cur->_data;
    pos->_pNext = cur->_pNext;
    free (cur);
    cur = NULL;
}

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

思路:和删除无头链表的非尾节点一样,可以在那个节点之后插入一个节点,然后再让两个节点交换数据

void InsertWithoutHead (Node *pos, DataType d)//在无头单链表的一个节点前插入一个节点
{
    DataType a;  //临时变量
    Node *NewNode = NULL;   //新节点
    assert (pos);   //指定位置不能为空

    //在指定位置的后面插入一个节点
    NewNode = BuyNode(d);
    NewNode->_pNext = pos->_pNext;
    pos->_pNext = NewNode;

    //再把这两的节点的数据交换
    a = pos->_data;
    pos->_data = NewNode->_data;
    NewNode->_data = a;

}

5、单链表实现约瑟夫环

思路:要用单链表实现约瑟夫环问题,首先要知道什么是约瑟夫环。约瑟夫环问题:有n(n>0)个人按顺时针围坐,取一个正整数k,从第一个人开始从1开始报数,报到k的人被淘汰,被淘汰的下一个人又从1开始报数……直到最后还剩一个人时停止报数。用单链表实现约瑟夫环,找到最后剩下的一个节点,先从头节点开始往后走k-1步,然后用删除无头单链表的非尾节点的方法删除这个节点,然后在继续往后走。直到最后还剩一个节点时(由于单链表是一个环,所以还剩一个节点时该节点和该节点是下一个节点相等)停止循环,再把最后一个节点的环解开,就实现的约瑟夫环。

Node* JosephCircle (pList **pHead, int k)   //实现约瑟夫环
{
    pList *cur = *pHead;
    assert (pHead);

    while (cur  != cur->_pNext)
    {
        pList *del = NULL;  //标记cur下一个节点
        int count = k;
        while (--count) //走k步
        {
            cur = cur->_pNext;
        }
        del = cur->_pNext;
        cur ->_data = del ->_data;
        cur ->_pNext = del ->_pNext;
        free (del);
        del = NULL;
    }
    *pHead = cur;   //解环
    cur->_pNext = NULL;
    return cur;
}

6、逆置单链表

void Reverse(pList **pHead) //逆置单链表
{
    pList *pre = NULL;
    pList *cur = *pHead;
    pList *next = NULL;
    assert (pHead);
    if (*pHead == NULL || (*pHead)->_pNext == NULL) //链表为空或者链表长度为1,不用逆置
    {
        return;
    }

    while (cur)
    {
        next = cur->_pNext;
        cur ->_pNext = pre;
        pre = cur;
        cur = next;
    }
    (*pHead) = pre;
}

7、单链表排序(冒泡)

void swap (DataType *p, DataType *q)
{
    DataType tmp = *p;
    *p = *q;
    *q = tmp;
}
void LinkListSort (pList **pHead)//单链表排序
{
    pList *tail = NULL;
    assert (pHead);

    if ((*pHead) == NULL || (*pHead)->_pNext == NULL )
    {
        return;
    }

    while (tail != (*pHead)->_pNext)
    {
        pList *pre = *pHead;
        pList *cur = (*pHead)->_pNext;
        while (cur != tail)
        {
            if (pre->_data > cur->_data)
            {
                swap (&(pre->_data), &(cur->_data));
            }
            pre = pre->_pNext;
            cur = cur->_pNext;
        }
        tail = pre;
    }
}

8、合并两个有序链表,合并后依然有序

Node *MergerLinkList (pList *pHead1, pList *pHead2)//合并两个单链表
{
    pList *cur1 = pHead1;
    pList *cur2 = pHead2;
    pList *pHead = NULL;
    pList *new_node = NULL;
    pList *tail = NULL;

    assert (pHead1 != NULL && pHead2 != NULL);

    if (pHead1 == NULL)
    {
        return pHead2;
    }
    else if(pHead2 == NULL)
    {
        return pHead1;
    }

    //先用其中一个链表的头作为新链表的头,哪一个头的数据小用哪一个做头
    if (pHead1->_data < pHead2->_data)
    {
        pHead = pHead1;
        pHead1 = pHead->_pNext;
    }
    else
    {
        pHead = pHead2;
        pHead2 = pHead2->_pNext;
    }

    tail = pHead;
    cur1 = pHead1;
    cur2 = pHead2;
    while (cur1 != NULL  &&  cur2 != NULL)
    {
        if (cur1->_data < cur2->_data)
        {
            tail ->_pNext = cur1;
            cur1 = cur1->_pNext;
        }
        else
        {
            tail ->_pNext = cur2;
            cur2 = cur2->_pNext;
        }
        tail = tail->_pNext;
    }

    if (cur1 == NULL)
    {
        tail->_pNext = cur2;
    }
    if (cur2 == NULL)
    {
        tail ->_pNext = cur1;
    }

    return pHead;
}

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

思路:用两个指针,一个快指针,一次走两步,一个慢指针,一次走一步,当快指针走到最后链表尾部时,慢指针刚好在中间节点

Node* FindMiddleNode (pList *pHead)//找链表中间节点
{
    pList *pFast = pHead;
    pList *pSlow = pHead;

    if (pHead == NULL || pHead->_pNext == NULL)
    {
        return pHead;
    }

    while (pFast && pFast->_pNext)
    {
        pFast = pFast->_pNext->_pNext;
        pSlow = pSlow->_pNext;
    }

    return pSlow;
}

10、查找单链表的倒数第k个节点,要求只能遍历一次链表

思路:两个指针,一个先往前走k-1步,一个指向头结点,然后在让两个指针同时往后走,先走的节点走完链表时,指向头结点的指针刚好走到倒数第k个节点的位置

Node* FindLastKNode (pList *pHead, int k)//找单链表的倒数第k个节点,只能遍历一次链表
{
    pList *cur = pHead;
    pList *pre = pHead;

    if (k <= 0)
    {
        printf ("k值有误!!\n");
        return NULL;
    }
    //先让cur往后走k-1步,再让pre和cur同时往后走,cur到最后一个节点时pre刚好在倒数第k个节点
    while (--k)
    {
        if (NULL == cur->_pNext)//如果k大于cur的长度,返回NULL;
        {
            printf ("k值有误!!\n");
            return NULL;
        }
        cur = cur->_pNext;
    }
    while (cur && cur->_pNext)
    {
        cur = cur->_pNext;
        pre = pre->_pNext;
    }
    return pre;
}

11、删除链表的倒数第k个节点

思路:用上面的方法找到倒数第k个节点,在删除该节点。

void DeleteLastKNode (pList **pHead, int k)//删除倒数第k个节点
{
    pList *pFast = *pHead;
    pList *pSlow = *pHead;
    pList *pre = NULL;

    assert (pHead != NULL && k > 0);
    if (NULL == *pHead)
    {
        printf ("链表为空!!!\n");
        return ;
    }
    //找倒数第k个节点
    while (--k)
    {
        if (pFast == NULL)
        {
            break;
        }
        pFast = pFast->_pNext;
    }
    if (pFast == NULL)
    {
        printf ("k值有误!!!\n");
        return;
    }
    while (pFast->_pNext)
    {
        pFast = pFast->_pNext;
        pre = pSlow;
        pSlow = pSlow->_pNext;
    }
    //1、删除的是第一个节点
    if (pSlow == *pHead)
    {
        *pHead = pSlow ->_pNext;
        free (pSlow);
        pSlow = NULL;
    }
    //2、删除的不是第一个节点
    else
    {
        pre ->_pNext = pSlow ->_pNext;
        free (pSlow);
        pSlow = NULL;
    }
}

12、判断单链表是否带环,若带环求环的长度,环的入口

思路:
1、判断链表是否带环:带环单链表中,两个指针,一个快指针一次走两步,一个慢指针一次走一步,同时从头节点开始走,两个指针在环内必会相遇。
2、求环入口:如果链表有环,则一定有:两个指针,一个从链表头部开始走,一个从链表中快慢指针交点开始走,两个指针的交点就是环入口

Node* LinkListHaveCircle(pList *pHead)//判断单链表是否带环,如果有环,返回环中的一个节点,如果没有环,返回NULL
{
    pList *pFast = pHead;
    pList *pSlow = pHead;

    if (pHead == NULL ) //如果链表为空,返回NULL
    {
        return NULL;
    }

    while (pFast && pFast->_pNext)//链表带环,两个指针,一个一次走一步,一个一次走两步,最终会相遇
    {
        pFast = pFast->_pNext ->_pNext;
        pSlow = pSlow->_pNext;
        if (pFast == pSlow)
        {
            return pFast;
        }
    }
    return NULL;
}

int LinkListWithCircleLen (pList *pHead)//求有环链表中环的长度
{
    pList *cur = LinkListHaveCircle(pHead); 
    pList *node = cur;
    int count = 1;
    if (pHead == NULL && cur == NULL)   //如果链表为空,长度为零;如果链表没有环,返回0
    {
        printf ("链表为空或者链表不存在环!!!\n");
        return 0;
    }

    while (node->_pNext != cur)
    {
        count ++;
        node = node->_pNext;
    }
    return count;
}

Node* CircleEntrance (pList *pHead) //求带环单链表环的入口
{
    pList *cur = LinkListHaveCircle(pHead); 
    pList *node = pHead;

    if (pHead == NULL && cur == NULL)   //如果链表为空,或者链表没有环,返回NULL
    {
        printf ("链表为空或链表不存在环!!!\n");
        return NULL;
    }

    while (node != cur) //如果链表有环,则一定有:两个指针,一个从链表头部开始走,一个从链表中快慢指针交点开始走,两个指针的交点就是环入口
    {
        node = node->_pNext;
        cur = cur->_pNext ;
    }
    return cur;
}

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

思路:判断两个链表是否相交,可以通过让第一个链表的尾指向第二个链表的头,如果构成一个带环单链表,就说明两个链表相交

Node *IsListintersect (pList* pHead1, pList* pHead2)//判断两个不带环的单链表是否相交,若相交,返回交点,若不想交,返回NULL
{
    pList *cur = pHead1;
    if (pHead1 == NULL || pHead2 == NULL)   //如果两个链表中有一个或者两个都为空,则无交点
    {
        return NULL;
    }
    //判断两个链表是否相交,可以通过让第一个链表的尾指向第二个链表的头,如果构成一个带环单链表,就说明两个链表相交
    //交点正好是环的入口点
    while (cur->_pNext != NULL)
    {
        cur = cur->_pNext;  //找第一个链表的尾节点
    }
    cur->_pNext = pHead2;
    if (LinkListHaveCircle(pHead1) != NULL)
    {
        return CircleEntrance (pHead1);
    }
    else
    {
        return NULL;
    }
}

14、判断两个链表是否相交,若相交,求交点(假设两个链表带环)

思路:带环单链表相交,只可能是两个单链表都带环,不存在一个不带环和一个带环的单链表相交的情况。每一个带环单链表中,两个指针,一个快指针一次走两步,一个慢指针一次走一步,同时从头节点开始走,两个指针在环内必会相遇。如果两个带环单链表相交,环一定是相交部分,也就是说两个链表的快慢指针交点在一个环里。所以判断两个链表是否相交(假设两个链表带环)可以先求出两个链表快慢指针的交点,在判断两个交点是否在一个环里。

Node* IsListintersectWithCircle (pList *pHead1, pList* pHead2)//判断两个带环的单链表是否相交,若相交,返回其中一个交点,若不想交,返回NULL
{
    pList *cur1 = LinkListHaveCircle(pHead1);//判断第一个链表是不是带环
    pList *cur2 = LinkListHaveCircle(pHead2);//判断第二个链表是不是带环
    pList *cur = NULL;

    if (cur1 == NULL)   //两个链表,一个带环一个不带环,不可能相交
        return NULL;

    if (cur2 == NULL)
        return NULL;

    //只有两个单链表都带环,才有可能相交,环是交点或交点的一部分
    //若两个环相交,则cur1和cur2一定在同一个环内
    cur = cur1;
    while (cur1->_pNext != cur)
    {
        if (cur1 == cur2)
        {
            return cur1;
        }
        cur1 = cur1->_pNext;
    }
    if (cur1 ->_pNext == cur)
    {
        if (cur1 == cur2)
            return cur1;
    }
    return NULL;
}

15、复杂链表的复制(复杂链表:一个链表的每个节点,有一个next指针指向下一个节点,一个random指针指向一个随机节点或NULL,复制后返回复制后的新链表)

思路:复杂链表的复制可分为三步:
1、先给链表每个元素后面插入一个与该元素相同的值
2、给插入新节点随机域赋值
3、将链表拆分成两个链表

CSLinkList *BuyNode_ComplexList(DataType d)//复杂链表创建新节点
{
    CSLinkList *cur = (CSLinkList *)malloc (sizeof (CSLinkList));

    if (cur == NULL)
    {
        perror ("BuyNode_ComplexList::malloc::");
    }

    cur->_data = d;
    cur->_next = NULL;
    cur->_random = NULL;

    return cur;
}
CSLinkList *CopyComplexLinkList (CSLinkList *pHead)//复制复杂链表
{
    CSLinkList *cur = pHead;
    CSLinkList *new_node = NULL;
    CSLinkList *new_list = NULL;
    if (pHead == NULL)  //链表为空,返回NULL
    {
        return NULL;
    }

    //给链表每个元素后面插入一个与该元素相同的值
    while (cur)
    {
        new_node = BuyNode_ComplexList(cur->_data);
        new_node ->_next = cur->_next;
        cur->_next = new_node;
        cur = cur ->_next ->_next;
    }

    //给插入新节点随机域赋值
    cur = pHead;

    while (cur)
    {
        new_node = cur->_next;
        if (cur->_random != NULL)
        {
            new_node->_random = cur->_random ->_next;
        }
        else
        {
            new_node->_random = NULL;
        }
        cur = new_node->_next;
    }

    //将链表拆分成两个链表
    cur = pHead;
    new_list = cur->_next;
    new_node = new_list;
    while (new_node)
    {
        cur->_next = new_node->_next;
        cur = new_node;
        new_node = new_node->_next;
    }
    return new_list;
}

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

void UnionSet(Node *l1, Node *l2)//求两个已排好序的单链表中的相同数据
{


    if (l1 == NULL || l2 == NULL)
    {
        printf ("链表为空!!\n");
        return;
    }

    while (l1 && l2)
    {
        if (l1->_data < l2->_data )
        {
            l1 = l1->_pNext;
        }
        else if (l1->_data > l2->_data)
        {
            l2 = l2->_pNext;
        }
        else
        {
            printf ("%d ",l1->_data);
            l1 = l1->_pNext;
        }
    }
    printf ("\n\n");

}

测试(test.c):

#include "LinkList.h"

void test_1()
{
    pList *plist = NULL;

    //尾部插入
    printf ("尾部插入:\n");
    PushBack (&plist, 4);
    PushBack (&plist, 1);
    PushBack (&plist, 3);
    PushBack (&plist, 2);
    //打印内容
    PrintLinkList (plist);
    //尾部删除
    printf ("尾部删除:\n");
    PopBack(&plist);
    PrintLinkList (plist);
    PopBack(&plist);
    PrintLinkList (plist);
    PopBack(&plist);
    PrintLinkList (plist);
    PopBack(&plist);
    PrintLinkList (plist);
    PopBack(&plist);
    PrintLinkList (plist);
    PopBack(&plist);
    PrintLinkList (plist);

    //头部插入
    printf ("头部插入:\n");
    PushFront(&plist, 1);
    PrintLinkList (plist);
    PushFront(&plist, 2);
    PrintLinkList (plist);
    PushFront(&plist, 3);
    PrintLinkList (plist);
    PushFront(&plist, 4);
    PrintLinkList (plist);

    //头部删除
    printf ("头部删除:\n");
    PopFront(&plist);
    PrintLinkList (plist);
    PopFront(&plist);
    PrintLinkList (plist);
    PopFront(&plist);
    PrintLinkList (plist);
    PopFront(&plist);
    PrintLinkList (plist);
    PopFront(&plist);
    PrintLinkList (plist);
    PopFront(&plist);
    PrintLinkList (plist);
    PopFront(&plist);
    PrintLinkList (plist);

    //头部插入
    printf ("头部插入:\n");
    PushFront(&plist, 1);
    PrintLinkList (plist);
    PushFront(&plist, 2);
    PrintLinkList (plist);
    PushFront(&plist, 3);
    PrintLinkList (plist);
    //链表销毁
    DestroyLInkLIst (&plist);
    PrintLinkList (plist);
}


void test_2()
{
    pList *plist = NULL;
    pList *ret = NULL;
    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, -2);
    PushBack (&plist, 3);
    PushBack (&plist, -4);
    PushBack (&plist, 5);
    PrintLinkList (plist);

    printf ("找数据为 -2 的节点>>\n");
    ret = FindNode(plist, -2);
    if (ret != NULL)
    {
        printf ("找到该节点了,_data = %d\n\n", ret->_data);
    }
    else
    {
        printf ("没有找到该节点!!\n\n");
    }

    printf ("找数据为 5 的节点>>\n");
    ret = FindNode(plist, 5);
    if (ret != NULL)
    {
        printf ("找到该节点了,_data = %d\n\n", ret->_data);
    }
    else
    {
        printf ("没有找到该节点!!\n\n");
    }
}

void test_3()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, -2);
    PushBack (&plist, 3);
    PushBack (&plist, -4);
    PushBack (&plist, 5);
    PrintLinkList (plist);

    printf ("在 5 前面插入一个 10:\n");
    Insert (&plist, FindNode(plist, 5), 10);
    PrintLinkList (plist);

    printf ("在 1 前面插入一个 -1:\n");
    /*Insert (&plist, FineNode(plist, 1), -1);*/
    Insert (&plist, plist, -1);
    PrintLinkList (plist);

    //Insert (&plist, NULL, 10); //assert (pos!=NULL)会报错
}

void test_4()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, -2);
    PushBack (&plist, 3);
    PushBack (&plist, -4);
    PushBack (&plist, 5);
    PrintLinkList (plist);

    printf ("删除1:\n");
    Erase (&plist, FindNode(plist, 1));
    PrintLinkList (plist);

    printf ("删除3:\n");
    Erase (&plist, FindNode(plist, 3));
    PrintLinkList (plist);


    printf ("删除5:\n");
    Erase (&plist, FindNode(plist, 5));
    PrintLinkList (plist);

    //printf ("删除15:\n");
    //Erase (&plist, FindNode(plist, 15));
    //传入的pos为NULL时assert会报错
    //PrintLinkList (plist);
}

void test_5()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 3);
    PushBack (&plist, 5);
    PrintLinkList (plist);

    printf ("删除1:\n");
    Remove(&plist, 1);
    PrintLinkList (plist);

    printf ("删除3:\n");
    Remove (&plist, 3);
    PrintLinkList (plist);

    printf ("删除3:\n");
    Remove (&plist, 3);
    PrintLinkList (plist);

    printf ("删除5:\n");
    Remove(&plist, 5);
    PrintLinkList (plist);

    printf ("删除5:\n");
    Remove(&plist, 5);
    PrintLinkList (plist);

}

void test_6()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 2);
    PushBack (&plist, 2);
    PushBack (&plist, 5);
    PushBack (&plist, 2);
    PushBack (&plist, 2);
    PushBack (&plist, 5);
    PrintLinkList (plist);

    printf ("删除所有2:\n");
    RemoveAll (&plist, 2);
    PrintLinkList(plist);
}

void test_7()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PrintLinkList (plist);

    printf ("删除1:\n");
    EraseNotTailNode (FindNode (plist, 1));
    PrintLinkList (plist);

    printf ("删除3:\n");
    EraseNotTailNode (FindNode (plist, 3));
    PrintLinkList (plist);

    printf ("删除2:\n");
    EraseNotTailNode (FindNode (plist, 2));
    PrintLinkList (plist);
}

void test_8()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PrintLinkList (plist);

    printf ("链表长度为:%d\n", GetLength(plist));
}

void test_9()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PrintLinkList (plist);

    PrintLinkListFromTailToHead (plist);
    printf ("NULL\n");
}

void test_10()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PrintLinkList (plist);

    printf ("在1前面插入一个3:\n");
    InsertWithoutHead (FindNode (plist, 1), 3);
    PrintLinkList (plist);

    printf ("在4前面插入一个2:\n");
    InsertWithoutHead (FindNode (plist, 4), 2);
    PrintLinkList (plist);
}

void test_11()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PushBack (&plist, 5);
    PushBack (&plist, 6);
    FindNode (plist, 6) ->_pNext = plist;

    JosephCircle (&plist, 3);
    PrintLinkList(plist);
}

void test_12()
{
    pList *plist = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PrintLinkList (plist);

    Reverse (&plist);
    PrintLinkList (plist);
}

void test_13()
{
    pList *plist = NULL;

    //尾部插入
    PushBack (&plist, 4);
    PushBack (&plist, 3);
    PushBack (&plist, 2);
    PushBack (&plist, 1);
    PrintLinkList (plist);
    printf ("从小到大排序:\n");
    LinkListSort (&plist);
    PrintLinkList (plist);
}

void test_14()
{
    pList *plist1 = NULL;
    pList *plist2 = NULL;
    pList *new_list = NULL;

    //尾部插入
    PushBack (&plist1, 5);
    PushBack (&plist1, 15);
    PushBack (&plist1, 88);
    PushBack (&plist1, 222);
    printf ("plist1:\n");
    PrintLinkList (plist1);

    PushBack (&plist2, 3);
    PushBack (&plist2, 13);
    PushBack (&plist2, 33);
    PushBack (&plist2, 38);
    PushBack (&plist2, 333);
    PushBack (&plist2, 334);
    printf ("plist2:\n");
    PrintLinkList (plist2);

    new_list = MergerLinkList (plist1, plist2);
    printf ("new_list:\n");
    PrintLinkList (new_list);
}

void test_15()
{
    pList *plist1 = NULL;
    pList *plist2 = NULL;

    PushBack (&plist1, 1);
    PushBack (&plist1, 2);
    PushBack (&plist1, 3);
    PushBack (&plist1, 4);
    PrintLinkList (plist1);
    printf ("plist1的中间节点为:%d\n\n", FindMiddleNode(plist1)->_data);

    PushBack (&plist2, 1);
    PushBack (&plist2, 2);
    PushBack (&plist2, 3);
    PushBack (&plist2, 4);
    PushBack (&plist2, 5);
    PrintLinkList (plist2);
    printf ("plist2的中间节点为:%d\n\n", FindMiddleNode(plist2)->_data);
}

void test_16()
{
    pList *plist = NULL;
    pList *ret = NULL;
    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PrintLinkList (plist);
    ret = FindLastKNode(plist, 3);
    if (ret != NULL)
    {
        printf ("链表倒数第3个节点为:%d\n\n", ret->_data);
    }
}

void test_17()
{
    pList *plist = NULL;
    pList *ret = NULL;
    int lenth = 0;

    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PushBack (&plist, 5);
    FindNode (plist,5)->_pNext = FindNode (plist, 2);
    if (LinkListHaveCircle(plist) != NULL)
    {
        printf ("有环,环内的一个点是:%d\n\n", LinkListHaveCircle(plist)->_data);
        //这个点是两个指针,从链表头开始往后走,一个一次走两步,一个一次走一步,在环内的交点;
        printf ("环的长度是:%d\n\n", LinkListWithCircleLen (plist));
        printf ("环的入口是:%d\n\n", CircleEntrance (plist)->_data);
    }
    else
    {
        printf ("没有环!!!\n");
    }
}

void test_18()
{
    pList *plist1 = NULL;
    pList *plist2 = NULL;
    pList *node = NULL;

    //尾部插入
    PushBack (&plist1, 1);
    PushBack (&plist1, 2);
    PushBack (&plist1, 3);
    PushBack (&plist1, 4);
    PushBack (&plist1, 5);

    PushBack (&plist2, 0);
    PushBack (&plist2, -1);
    PushBack (&plist2, -2);
    FindNode(plist2, -2)->_pNext =FindNode (plist1, 3);

    PrintLinkList (plist1);
    PrintLinkList (plist2);

    node = IsListintersect (plist1, plist2);
    if (node != NULL)
    {
        printf ("两个链表相交,交点为:%d\n\n", node->_data);
    }
    else
    {
        printf ("两个链表不相交!!\n\n");
    }
}

void test_19()
{
    pList *plist1 = NULL;
    pList *plist2 = NULL;
    pList *ret = NULL;

    //尾部插入
    PushBack (&plist1, 1);
    PushBack (&plist1, 2);
    PushBack (&plist1, 3);
    PushBack (&plist1, 4);
    PushBack (&plist1, 5);
    FindNode (plist1,5)->_pNext = FindNode (plist1, 3);

    PushBack (&plist2, -1);
    PushBack (&plist2, -2);
    FindNode (plist2, -2)->_pNext = FindNode (plist1, 2);

    ret = IsListintersectWithCircle(plist1, plist2);
    if (ret != NULL)
    {
        printf ("两个链表有交点,其中一个交点是:%d\n\n", ret->_data);
    }
    else
    {
        printf ("两个链表没有交点!!!\n\n");
    }
}

void test_20()
{
    CSLinkList node1, node2, node3,node4;
    CSLinkList *pHead = NULL;
    node1._data = 1;
    node1._next = &node2;
    node1._random = &node3;

    node2._data = 2;
    node2._next = &node3;
    node2._random = &node1;

    node3._data = 3;
    node3._next = &node4;
    node3._random = &node3;

    node4._data = 4;
    node4._next =NULL;
    node4._random = NULL;

    pHead = CopyComplexLinkList (&node1);
}
void test_21()
{
    pList *plist1 = NULL;
    pList *plist2 = NULL;

    PushBack (&plist1, 1);
    PushBack (&plist1, 2);
    PushBack (&plist1, 2);
    PushBack (&plist1, 4);
    PrintLinkList (plist1);

    PushBack (&plist2, 1);
    PushBack (&plist2, 2);
    PushBack (&plist2, 2);
    PushBack (&plist2, 4);
    PushBack (&plist2, 5);
    PrintLinkList (plist2);

    printf ("连个链表中相同的数据有:\n");
    UnionSet(plist1, plist2);

}

void test_22()
{
    pList *plist = NULL;
    int k = 0;
    //尾部插入
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PushBack (&plist, 5);
    PrintLinkList (plist);
    printf ("删除倒数第%d个节点:\n", k);
    DeleteLastKNode (&plist, k);
    PrintLinkList (plist);
}
void test_23()
{
    pList *plist = NULL;
    int k = 0;
    PushBack (&plist, 1);
    PushBack (&plist, 2);
    PushBack (&plist, 3);
    PushBack (&plist, 4);
    PushBack (&plist, 5);
    PrintLinkList (plist);
    printf ("获取单链表最后一个节点 :\n"); 
    PrintLinkList ( LinkListBack(plist));

}
int main ()
{
    test_1();//测试头插,头删,尾插,尾删,销毁链表

    test_2();//测试寻找节点

    test_3();//测试指定位置前插入

    test_4();//指定位置删除

    test_5();//删除指定元素第一次出现的节点

    test_6();//删除出现指定元素的所有节点

    test_7();////删除指定位置的非尾节点

    test_8();//获取链表长度

    test_9();//逆序打印单链表

    test_10();//在无头单链表的一个节点前插入一个节点

    test_11();//约瑟夫环

    test_12();//单链表逆置

    test_13();//单链表的冒泡排序

    test_14();//合并两个有序单链表,合并之后的单链表也有序

    test_15();//找单链表的中间节点,只能遍历一次链表

    test_16();//找单链表的倒数第k个节点,只能遍历一次链表

    test_17();//判断单链表是否带环,若带环,求环的交点和入口

    test_18();//判断两个不带环的单链表是否相交,若相交,求交点

    test_19();//判断两个带环的单链表是否相交,若相交,求交点

    test_20();//复制复杂链表

    test_21();//求两个已排序好的单链表中的相同数据

    test_22();//删除倒数第K的节点(倒数第三个)

    test_23();// 获取单链表最后一个节点 

    system ("pause");

    return 0;
}

猜你喜欢

转载自blog.csdn.net/dream8834/article/details/81612430