数据结构之链表面试题(约瑟夫环,判断链表带环求交点问题)

#pragma once

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int DataType;

typedef struct Node
{
    struct Node* _pNext;
    DataType _data;
}Node, *PNode;

// 删除链表的非尾结点,要求不能遍历链表 
void DeleteNotTailNode(PNode pos);
// 在链表pos位置前插入值为data的结点 
void InsertPosFront(PNode pos, DataType data);
// 约瑟夫环 
void JosephCircle(PNode* pHead, const int M);
// 使用冒泡方式对单链表进行排序 
void BubbleSort(PNode pHead);
// 单链表的逆序---三个指针 
void ReverseSList(PNode* pHead);
// 单链表的逆序---使用头插法 
PNode ReverseSListOP(PNode pHead);
// 合并两个有序链表,合并起来依然要有序 
PNode MergeSList(PNode pHead1, PNode pHead2, PNode pHead3);
// 查找链表的中间结点,要求只能遍历一次链表 
PNode FindMiddleNode(PNode pHead);
// 查找链表的倒数第K个结点 
PNode FindLastKNode(PNode pHead, int K);
// 判断单链表是否相交?链表不带环 
int IsCrossWithoutCircle(PNode pHead1, PNode pHead2);
// 求不带环单链表相交交点 
PNode GetCrossNode(PNode pHead1, PNode pHead2);
// 判断链表是否带环 
PNode IsCircle(PNode pHead);
// 求环的长度 
int GetCircleLen(PNode pHead);
// 求环的入口点
PNode GetEnterNode(PNode pHead, PNode pMeetNode);
// 判断链表是否带环,链表可能带环 
int IsListCrossWithCircle(PNode pHead1, PNode pHead2);
// 删除链表的非尾结点,要求不能遍历链表 

void DeleteNotTailNode(PNode pos)
{
    PNode pCur = NULL;
    if (pos)
    {
        pCur= pos->_pNext;
        pos->_data = pCur->_data;
        pos->_pNext = pCur->_pNext;
        free(pCur);
    }
}
// 在链表pos位置前插入值为data的结点 
void InsertPosFront(PNode pos, DataType data)
{
    PNode pCur = NULL;
    if (!pos)
    {
        pCur = BuySListNode(pos->_data);//建立一个新节点
        pos->_data = data;
        pCur->_pNext = pos->_pNext;
        pos->_pNext = pCur;
    }
}
// 约瑟夫环 
void JosephCircle(PNode* pHead, const int M)
{
    PNode pCur = NULL;
    PNode pDel = NULL;
    assert(pHead);
    if (NULL == pHead)
        return;
    pCur = pHead;
    //构环
    while (pCur->_pNext)
    {
        pCur = pCur->_pNext;
    }
    pCur->_pNext =pHead ;
    pCur = pHead;
    while (pCur != pCur->_pNext)
    {
        //记数
        int count = M;
        while (--count)
        {
            pCur = pCur->_pNext;
        }
        //删节点 
        pDel = pCur->_pNext;
        pCur->_data = pDel->_data;
        pCur->_pNext = pDel->_pNext;
        free(pDel);
    }
    //解环 
    pCur->_pNext = NULL;
    return pCur;
}
// 使用冒泡方式对单链表进行排序 
void BubbleSort(PNode pHead)
{
    PNode cur = NULL;
    PNode tail = NULL;

    assert(pHead);

    cur = pHead->_pNext;

    while (cur != tail)
    {
        while (cur->_pNext != tail)
        {
            if (cur->_data > cur->_pNext->_data)
            {
                DataType tmp = cur->_data;
                cur->_data = cur->_pNext->_data;
                cur->_pNext->_data = tmp;
            }
            cur = cur->_pNext;
        }
        tail = cur;
        cur = pHead->_pNext;
    }
    return;

}
// 单链表的逆序---三个指针 
void ReverseSList(PNode* pHead)
{
    PNode p1, p2, p3;
    assert(pHead);
    if (NULL == pHead)
        return pHead;
    p1 = pHead;
    p2 = p1->_pNext;
    while (p2)
    {
        p3 = p2->_pNext;
        p2->_pNext = p1;
        p1 = p2;
        p2 = p3;
    }
    (*pHead)->_pNext = NULL;
    *pHead = p1;
    return pHead;
}
// 单链表的逆序---使用头插法 
PNode ReverseSListOP(PNode pHead)
{
    PNode pCur = NULL;
    PNode p = NULL;
    assert(pHead);
    if (NULL == pHead)
        return pHead;
    pCur = pHead->_pNext;   // 创建一个空表
    pHead->_pNext = NULL;
    while (!pCur)
    {
        p = pCur->_pNext;
        pCur->_pNext = pHead->_pNext;
        pHead->_pNext = pCur;
        pCur = p;
    }
}
// 合并两个有序链表,合并起来依然要有序 
PNode MergeSList(PNode pHead1, PNode pHead2,PNode pHead3)
{
    PNode pa=NULL;    //pa pb分别指向 pHead1 和 pHead2 中待比较插入的节点
    PNode pb=NULL;
    PNode pc=NULL;    //pc指向 pHead3 最后一个节点
    pa = pHead1->_pNext;
    pb = pHead2->_pNext;
    pHead3 = pc = pHead1;
    while (pa && pb)
    {
        if (pa->_data <= pb->_data)//将pa所指的节点连接到pc所指向的节点后面
        {
            pc->_pNext = pa;
            pc = pa;
            pa = pa->_pNext;
        }
        else                       //将pb所指向的节点连接到pc所指向的节点后面
        {
            pc->_pNext = pb;
            pc = pb;
            pb = pb->_pNext;
        }
    }
    pc->_pNext = pa ? pa : pb;
    free(pHead2);
}
// 查找链表的中间结点,要求只能遍历一次链表 
PNode FindMiddleNode(PNode pHead)
{
    assert(pHead);
    PNode pFast = pHead;
    PNode pSlow = pHead;
    while (NULL != pHead || NULL != pHead->_pNext)
    {
        pFast = pFast->_pNext->_pNext;
        pSlow = pSlow->_pNext;
    }
    return pSlow;
}
// 查找链表的倒数第K个结点 
PNode FindLastKNode(PNode pHead, int K)
{
    PNode pFast = NULL;
    PNode pSlow = NULL;
    assert(pHead);
    if (NULL == pHead)
        return 0;
    pFast = pHead;
    pSlow = pHead;
    int count = 0;
    while (NULL != pFast) //遍历一次,让指针走到最后一个结点
    {                     
        if (count < K)   //若是count<k,让快指针先走到k节点处
        {
            count++;
            pFast = pFast->_pNext;
        }
        else         //让快慢指针一块儿走
        {
            pSlow = pSlow->_pNext;
            pFast = pFast->_pNext;
        }
    }
    if (count < K)
        return 0;  //找不到返回0
    else
        printf("%d", pSlow->_data);  //找到返回1,并打印 k 结点值
    return 1;
}

// 判断单链表是否相交?相交求交点,链表不带环 
int IsCrossWithoutCircle(PNode pHead1, PNode pHead2)
{
    assert(pHead1);
    assert(pHead2);
    PNode pTail1 = pHead1;
    PNode pTail2 = pHead2;
    if (NULL == pHead1 || NULL == pHead2);
    return 0;
    while (pTail1 && pTail1->_pNext)
    {
        pTail1 = pTail1->_pNext++;  
    }
    while (pTail2 && pTail2->_pNext)
    {
        pTail2 = pTail2->_pNext++;
    }
    if ((pTail1 == pTail2) && (NULL != pTail1))
        return 1;
    else
        return NULL;
}
// 求不带环单链表相交交点 
PNode GetCrossNode(PNode pHead1, PNode pHead2)
{
    assert(pHead1);
    assert(pHead2);
    PNode pTail1 = pHead1;
    PNode pTail2 = pHead2;
    DataType len1 = 0;
    DataType len2 = 0;
    DataType Sub = 0;
    if (NULL == pHead1 || NULL == pHead2);
    return 0;
    while (pTail1 && pTail1->_pNext)
    {
        pTail1 = pTail1->_pNext;
        len1++;
    }
    while (pTail2 && pTail2->_pNext)
    {
        pTail2 = pTail2->_pNext;
        len2++;
    }
    if (pTail1 != pTail2)
    {
        return NULL;
    }
    Sub = len1 - len2;
    if ((0 == Sub) && (pTail1 == pTail2))   //pTail1 和pTail2长度相等
    {
        return 1;
    }
    else if (Sub > 0)  //pTail1比pTail2长 ,让pTail2先走他们的Sub步,而后一块儿走
    {
        while (Sub != 0)
        {
            pTail2 = pTail2->_pNext;
            Sub--;
        }
        while (pTail1 != pTail2)
        {
            pTail1 = pTail1->_pNext;
            pTail2 = pTail2->_pNext;
        }
        return 1;
    }
    else   //pTail2比pTail1长 ,让pTail1先走他们的Sub步,而后一块儿走
    {
        while (Sub !=0)
        {
            pTail1 = pTail1->_pNext;
            Sub--;
        }
        while (pTail1 != pTail2)
        {
            pTail1 = pTail1->_pNext;
            pTail2 = pTail2->_pNext;
        }
        return 1;
    }
}
// 判断链表是否带环 
PNode IsCircle(PNode pHead)
{
    assert(pHead);
    PNode pFast = pHead;
    PNode pSlow = pHead;
    while (pFast && pFast->_pNext)
    {
        pFast = pFast->_pNext->_pNext;
        pSlow = pSlow->_pNext;
    }
    if (pFast == pSlow)
    {
        printf("链表带环\n");
        return pSlow;
    }
    else
        return NULL;
}
// 求环的长度 
int GetCircleLen(PNode pHead)
{
    DataType i = 0;
    PNode pCur = pHead;
    while (pCur->_pNext != pHead)
    {
        ++i;
        pCur = pCur->_pNext;
    }
    return i;
}
// 求环的入口点--注意推断过程 
//链表总长度=起始点到入口点长度+环的长度
//有结论得出:起始点到入口点长度 == 相遇点的长度到入口点的长度
PNode GetEnterNode(PNode pHead, PNode pMeetNode)
{
    PNode pEnterNode = NULL;
    PNode pCur1 = pHead;
    PNode pCur2 = pMeetNode;
    while (pCur1 != pCur2)
    {
        pCur1 = pCur1->_pNext;
        pCur2 = pCur2->_pNext;
    }
    pEnterNode = pCur1;
    return pEnterNode;
}
//判断链表是否带环,链表可能带环 
int IsListCrossWithCircle(PNode pHead1, PNode pHead2)
{
    assert(pHead1);
    assert(pHead2);
    //判断两个链表是否带环
    PNode pCur1 = IsCircle(pHead1);
    PNode pCur2 = IsCircle(pHead2);
    PNode pEnter1 = NULL;
    PNode pEnter2 = NULL;
    PNode pCrossNode = NULL;
    if (NULL == pCur1 && NULL == pCur2)//两个都不带环
    {
        pCrossNode = GetEnterNode(pCur1, pCur2); //相交求交点
        return 0;
    }
    else if ((NULL == pCur1 && NULL != pCur2) || (NULL != pCur1 && NULL == pCur2))//若是一个带环一个不带环
    {
        return -1;
    }
    else                              //两个都带环
    {
        PNode pMeet = pCur1->_pNext;
        while (pMeet !=pCur1)
        {
            if (pMeet == pCur2)//两个环相交
            {
                pEnter1 = GetEnterNode(pHead1, pCur1);
                pEnter2 = GetEnterNode(pHead2, pCur2);
                if (pEnter1 == pEnter2)
                {
                    printf("环外相交\n");
                    pCrossNode = GetEnterNode(pCur1, pCur2); //相交求交点
                    return 1;
                }
                else
                {
                    printf("环内相交\n");
                    return 2;
                }
            }
            pMeet = pMeet->_pNext;
        }
        return -1;   //不相交
    }
}