c++ 怎样连接两个链表_一文搞定链表编程题

839c90325264819047d18c6a3074453e.png
链表,作为一项基本的数据结构,简单,又复杂,在面试中经常被问到,所有有必要一文搞定。链表,是一种动态数据
结构,因为在创建链表的时候,我们不需要知道链表的长度,当插入一个结点时,只需要为该结点分配内存,
然后调整指针的指向来确保新结点被连接到链表中。

(0)链表反转

class SListNode(){
    SListNode pnext=null;
    int val;
    SlistNode(int val){this.val = val}

}

public ListNode reverseList(ListNode head){
    if(head == null) return null;
    ListNode pre = null;
    ListNode next = null;
    while(head){
        next = head.next;
        head.next = pre;
        pre = head;
        head = next;
}
    return pre ;
}

(1)不遍历链表删除非尾结点

思路:通常我们做链表结点删除的时候,都需要知道目标结点的前驱,让前驱结点的next指向目标结点的next,然后释放目标结点,这种方法需要遍历链表。当不能遍历时,我们就需要用后一个结点来覆盖目标结点,即:将后一个结点的data复制到目标结点中,然目标结点的next,指向后一个结点的next,返回释放后一个结点。

void RemoveNodeNotTail(SListNode **ppFirst, SListNode *pos)
{
	SListNode *pDel = pos;
        SListNode *pNode = pDel ->next;
        pos->data = pNode->data;
        pos->next = pNode->next;
        free(pNode);


}

2、遍历一次,找到中间结点

只遍历一遍要找到中间节点,我们可以定义一快一慢两个指针pFast和pSlow,让他们同时从链表的头结点开始向后遍历,慢指针一次向后走一步,快指针一次向后走两步,当pFast == NULL时,pSlow所指向的位置即为链表的中间节点。

SListNode* FindMid(SListNode *pFirst)
{
	SListNode *pSlow = pFirst;
	SListNode *pFast = pFirst->pNext;
	assert(pFirst);
	while(pFast)
	{
		pSlow = pSlow->pNext;
		pFast = pFast->pNext->pNext;
	}
	pSlow->pNext = NULL;

	return pSlow;
}
方法2
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
}

3、遍历一次,找到倒数第K个结点(k从1开始)

原理:原理同遍历一次找到中间结点,定义一块,一慢两个指针,让快指针,先走k-1 步,然后两个指针同时向后遍历,当快指针==null时,慢指针指向的位置即为倒数第k个结点所在的位置。为了符合大多数人的习惯,本题从 1 开始计数,即链表的尾结点是倒数第 1 个结点。

public static ListNode findKthToTail(ListNode head, int k) {
        // 输入的链表不能为空,并且k大于0
        if (k < 1 || head == null) {
            return null;
        }
        // 指向头结点
        ListNode pointer = head;
        // 倒数第k个结点与倒数第一个结点相隔k-1个位置
        // pointer先走k-1个位置
        for (int i = 1; i < k; i++) {
            // 说明还有结点
            if (pointer.next != null) {
                pointer = pointer.next;
            }
            // 已经没有节点了,但是i还没有到达k-1说明k太大,链表中没有那么多的元素
            else {
                // 返回结果
                return null;
            }
        }
        // pointer还没有走到链表的末尾,那么pointer和head一起走,
        // 当pointer走到最后一个结点即,pointer.next=null时,head就是倒数第k个结点
        while (pointer.next != null) {
            head = head.next;
            pointer = pointer.next;
        }
        // 返回结果
        return head;
    }

ListNode* FindKthToTail(ListNode* pListHead,unsigned int k)
{
    if(pListHead == NULL)
        return NULL;
    
    ListNode* pFast = pListHead;
    ListNode* pSlow = pListHead;
    
    for(unsigned int i=0;i<k;i++)
    {
        if(pFast->next)
            pFast = pFast->next;
        else
            return NULL;
    }
    
    while(pFast->next)
    {
        pFast = pFast->next;
        pSlow = pSlow->next;
    }
    
    retrn pSlow;
}

(4)删除倒数第K个结点

思路:找打导出第k+1 个结点,即可

void RemoveK(SListNode *pFirst, int k)
{
	SListNode *pSlow = pFirst;
	SListNode *pFast = pFirst;
	SListNode *pDel;

	while(k)
	{
		pFast = pFast->pNext;
		k--;
	}

	while(pFast)
	{
		pSlow = pSlow->pNext;
		pFast = pFast->pNext;
	}

	pDel = pSlow->pNext;
	pSlow->pNext = pDel->pNext;
	free(pDel);
}

(5)判断链表是否带环;若带环,求环的长度和入口点

要判断一个链表是否带环,只需要定义一快一慢两个指针pFast和pSlow,让他们同时从链表的头结点开始向后遍历,慢指针一次向后走一步,快指针一次向后走两步,若两个指针最终相遇,则链表带环;若快指针遍历到NULL,则表明链表不带环。

53deb397e8f1e9c9effe215e6c0e11b9.png

当pSlow和pFast第一次相遇时,pSlow走过的路程为l + a,pFast走过的路程为l + 2*a + c,又因为我们知道pFast的速度为pSlow的两倍,因此可得出l = c,此时我们只需要定义一个pMeet指针从头节点出发开始遍历,pSlow从相遇点出发继续遍历,当两个指针相遇时,所指向的节点即为环的入口节点。

知道了环的入口节点,那么环的长度就十分好求了。用pMeet指针记录环的入口节点,pSlow从入口节点出发向后遍历,当两个指针再次相遇时,pSlow所走过的路径即为环的长度。

void IsLoopSList(SListNode *pFirst)
{
	SListNode *pSlow = pFirst;
	SListNode *pFast = pFirst->pNext->pNext;
	SListNode *pEnter = (SListNode*)malloc(sizeof(SListNode));
	int flag = 1;
	int LoopLen = 0;
	while((pSlow != pFast) && (pFast != NULL))
	{
                //这里是因为,pFast已经走了,而low开没走,所有low也要走一步
		if(flag)
		{
			pSlow = pSlow->pNext;
			flag = 0;
			continue;
		}
		pSlow = pSlow->pNext;
		pFast = pFast->pNext->pNext;
	}
	
	if(pFast != NULL)
	{
		SListNode *pMeet = pSlow;
		SListNode *pNode = pFirst;
		while(pNode != pMeet)
		{
			pNode = pNode->pNext;
			pMeet = pMeet->pNext;
		}
		pEnter->data = pMeet->data;
		pEnter->pNext = NULL;
		printf("该链表带环,环的入口点为:> ");
		PrintSList(pEnte);
                pFast = pFast->pNext->pNext;

                while(pSlow != pFast){
                        if(LoopLen == 0)
			{
				pSlow = pSlow->pNext;
				LoopLen++;
				continue;
			}
			pSlow = pSlow->pNext;
			pFast = pFast->pNext->pNext;
			LoopLen++;
		}
		printf("环的长度为:> %dn", LoopLen);
		return;
	}
 
	printf("该链表不带环n");
 
}

备注:

单链表常见面试题_Python_Dxx_xx4的博客-CSDN博客​blog.csdn.net
554510b6d22f79e79b0ac4d6ac5280dd.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值