算法与数据结构基础系列(一): 链表的常见问题分析及实现

本文对链表的常见问题进行总结和归纳:

定义链表的数据结构如下:

struct ListNode{
        int value;
        ListNode *next;
}
  • 问题一: 求单链表中节点的个数
    unsigned int GetListLength(ListNode *pHead) {
    	if (pHead == NULL) { //判断是否为空链表
    		return 0;
    	}
    
    	unsigned int ListLength = 0;
    	ListNode * pCurrent = pHead;
    	while (pCurrent) {
    		ListLength++;
    		pCurrent = pCurrent->next;
    	}
    	return ListLength;
    }
    
  • 问题二: 单链表的原地翻转
    ListNode * RevertList(ListNode * pHead) {
    	if (pHead == NULL || pHead->next == NULL) {
    		return pHead;
    	}
    	ListNode * pRevertListHead = NULL;
    	ListNode * pCurrent = pHead;
    
    	while (pCurrent){
    		ListNode * pTemp = pCurrent;
    		pCurrent = pCurrent->next;
    		pTemp->next = pRevertListHead;
    		pRevertListHead = pTemp;
    	}
    	return pRevertListHead;
    }
    


  • 问题三: 求链表的倒数第K个节点

    ListNode * GetKthNode(ListNode * pHead, unsigned int k) {
    
    	if (pHead == NULL || k == 0) {
    		return NULL;
    	}
    
    	ListNode * pAhead = pHead;
    	ListNode * pBehand = pHead;
    
    	for (int i = 1; i < k; i++) {
    		if (pAhead->next) {
    			pAhead = pAhead->next;
    		}
    		else { // 节点数小于k
    			return NULL;
    		}
    	}
    
    	while (pAhead->next != NULL) { // 两个指针同时移动,当第一个指针指向链表尾部的时候,第二个指针刚好走到倒数第k个节点
    		pAhead = pAhead->next;
    		pBehand = pBehand->next;
    	}
    
    	return pBehand;
    }
    
  • 问题四:求一个链表的中间节点

    ListNode * FindMidNode(ListNode * pHead) {
    
    	if (pHead == NULL || pHead->next == NULL) { // 空链表或者链表节点数为1,返回NULL
    		return NULL;
    	}
    
    	// 初始化
    	ListNode * pAhead = pHead;
    	ListNode * pBehind = pHead;
    
    	// 一个指针每次前进两步,一个指针每次前进一步,当走两步的指针走到终点时,走一步的指针指向为中间节点
    	while (pAhead->next) {
    		pAhead = pAhead->next;
    		pBehind = pBehind->next;
    		if (pAhead->next) {
    			pAhead = pAhead->next;
    		}
    	}
    	return pBehind;
    
    }
    
  • 问题五:合并两个有序的链表
    // 合并两个有序的链表
    ListNode * MergeTwoSortedListNode(ListNode * pHead1, ListNode * pHead2) {
    
    	// 特殊情况处理
    	if (pHead1 == NULL) {
    		return pHead2;
    	}
    	if (pHead2 == NULL) {
    		return pHead1;
    	}
    
    	// 合并后的头指针
    	ListNode * pMerged = NULL;
    
    	if (pHead1->value <= pHead2->value) {
    		pMerged = pHead1;
    		pHead1 = pHead1->next;
    	}else {
    		pMerged = pHead2;
    		pHead2 = pHead2->next;
    	}
    
    	pMerged->next = NULL;
    
    	ListNode * pTemp = pMerged; // pTemp用于指向最新合并的节点
    	while (pHead1 != NULL && pHead2 != NULL) {
    		if (pHead1->value <= pHead2->value) {
    			pTemp->next = pHead1;
    			pHead1 = pHead1->next;
    			pTemp = pTemp->next;
    		}else {
    			pTemp->next = pHead2;
    			pHead2 = pHead2->next;
    			pTemp = pTemp->next;
    		}
    		pTemp->next = NULL;
    	}
    	
    	// 循环结束后将 pTemp 值设置为当前指针不为空的链表指针
    	if (pHead1) {
    		pTemp = pHead1;
    	}
    	else if(pHead2) {
    		pTemp = pHead2;
    	}
    
    	return pMerged;
    }
    
  • 问题六: 判断一个链表是否有环
    // 判断一个链表是否有环
    bool IsCircle(ListNode *pHead) {
    	ListNode * pFast = pHead;
    	ListNode * pSlow = pHead;
    	while (pFast->next != NULL && pFast != NULL) {
    		pFast = pFast->next->next;
    		pSlow = pSlow->next;
    		if (pFast == pSlow) {
    			return true;
    		}
    	}
    	return false;
    }
    
  • 问题七:判断两个链表是否相交

    // 判断两个链表是否相交
    bool IsCrossing(ListNode * pFistHead, ListNode * pSecondHead) {
    	if (pFistHead == NULL || pSecondHead == NULL) {
    		return false;
    	}
    	
    	ListNode *ptemp1 = pFistHead;
    	ListNode *ptemp2 = pSecondHead;
    	
    	while (ptemp1 != NULL) {
    		ptemp1 = ptemp1->next;
    	}
    
    	while (ptemp2 != NULL) {
    		ptemp2 = ptemp2->next;
    	}
    	return ptemp1 == ptemp2;
    }
    
  • 问题八:求两个相交链表相交的第一个节点

    // 找出两个相交链表的第一个相同的节点
    ListNode * FindTheFirstCommonNode(ListNode * pHead1, ListNode * pHead2) {
    	if (pHead1 == NULL || pHead2 == NULL) { // 其中一个链表为空
    		return NULL;
    	}
    	
    	int length1 = 1;
    	int length2 = 1;
    	int length;
    	ListNode * pTemp1 = pHead1;
    	ListNode * pTemp2 = pHead2;
    
    	while (pTemp1 != NULL) {
    		pTemp1 = pTemp1->next;
    		length1++;
    	}
    
    	while (pTemp2 != NULL) {
    		pTemp2 = pTemp2->next;
    		length2++;
    	}
    
    	if (pTemp1 != pTemp2) { // 两个链表不相交
    		return NULL;
    	}
    
    	pTemp1 = pHead1;
    	pTemp2 = pHead2;
    
    	if (length1 > length2) {
    		length = length1 - length2;
    		while (length > 0) {
    			pTemp1 = pTemp1->next;
    			length--;
    		}
    	}
    	else {
    		length = length2 - length1;
    		while (length > 0) {
    			pTemp2 = pTemp2->next;
    			length--;
    		}
    	}
    
    	while (pTemp1 != pTemp2) {
    		pTemp1 = pTemp1->next;
    		pTemp2 = pTemp2->next;
    	}
    
    	return pTemp1;
    
    }
    
  • 问题九:求一个带环链表的环的入口

    如上图所示,当环的长度(b+c),大于进入环之前的长度(a)时,我们可以用一快一慢两个指针分别从头开始前进,快指针一次前进两步,慢的指针一次前进一步。当两个指针第一次相遇时z点,快指针走过的距离刚好是慢指针的两倍,那么快指针走过的长度为 a+b+c+b,慢指针走过的长度为 a+b,根据  a+b+c+b = 2(a+b),显然就有 a = c,即在相遇的地点,让慢指针从头开始走,快指针每次前进一步,再次相遇的地点即为环的入口。当然,当环的长度小于进入环之前的长度,其实也是满足的,不同之处在于,相遇前快指针会绕环 n 周,这个可以从数学上去证明,下面给出代码实现:

    // 求有环链表的第一个节点
    ListNode * FirstListNode(ListNode * pHead) {
    	if (pHead == NULL || pHead->next == NULL) {
    		return NULL;
    	}
    	
    	ListNode * pFast = pHead;
    	ListNode * pSlow = pHead;
    	
    	while (pFast != NULL && pFast->next != NULL) {
    		pFast = pFast->next->next;
    		pSlow = pSlow->next;
    		if (pFast == pSlow) { // 有环
    			pSlow = pHead;
    			while (pSlow != pFast) {
    				pFast = pFast->next;
    				pSlow = pSlow->next;
    			}
    			return pFast;
    		}
    	}
    	return NULL;
    }
    


  • 问题十:
原文地址:https://www.cnblogs.com/smallrookie/p/6476776.html