1. 程式人生 > >連結串列的各種基本操作

連結串列的各種基本操作

1. 連結串列定義。

#ifndef _LISTNODE_
#define _LISTNODE_

struct ListNode
{
	int m_value;
	ListNode * m_pNext;
};

#endif

2. 初始化連結串列。

void InitList(ListNode * L)
{
	L = nullptr;
	cout << "InitList Success! " << endl;
}

3. 建立連結串列。共三種方法。

// ------建立線性表 1: 頭結點插入法,頭結點不放資料------
ListNode * CreateListFromHead()
{
	ListNode * pHead = new ListNode;
	pHead->m_pNext = nullptr;
	if (pHead == nullptr)
	{
		cout << "分配記憶體失敗!" << endl;
		return nullptr;
	}

	int input;
	
	while (cin>>input && input != -1)
	{
		ListNode *pTmp = new ListNode;
		pTmp->m_value = input;
		pTmp->m_pNext = pHead->m_pNext;   //將節點插入到表頭,這是頭插法
		pHead->m_pNext = pTmp;
		pTmp = nullptr;
	}

	//pTmp = nullptr;
	return pHead;
}
// ------建立線性表 2: 尾插法,頭結點不放資料------
ListNode * CreateListFromTail2()
{
	ListNode * pHead = new ListNode;
	if (pHead == nullptr)
	{
		cout << "分配記憶體失敗!" << endl;
		return nullptr;
	}
	pHead->m_pNext = nullptr;

	ListNode *pTail = new ListNode;
	pTail = pHead;

	int input;

	while (cin>>input && input != -1)
	{
		ListNode *pTmp = new ListNode;
		pTmp->m_value = input;
		pTail->m_pNext = pTmp;   //尾插法
		pTail = pTmp;
		pTmp = nullptr;
	}
	pTail->m_pNext = nullptr;

	return pHead;
}
// ------建立線性表 3: 尾插法,無頭結點------
ListNode * CreateListFromTail()
{
	/*ListNode * pHead = new ListNode;
	if (pHead == nullptr)
	{
		cout << "分配記憶體失敗!" << endl;
		return nullptr;
	}*/
	ListNode *pHead = nullptr;

	ListNode *pTail = pHead;

	int input;

	while (cin>>input && input != -1)
	{
		ListNode *pTmp = new ListNode;
		pTmp->m_value = input;

		if (pHead == nullptr)
			pHead = pTmp;
		else
			pTail->m_pNext = pTmp;   //尾插法
		pTail = pTmp;

		pTmp = nullptr;
	}
	if (pTail != nullptr)
		pTail->m_pNext = nullptr;

	return pHead;
}

4. 插入。共兩種方法。

// -------------------在pos位置的前方插入-----------------------------
void InsertListNodeFromFront(ListNode *&L, int pos, int value)  //可能需要改變頭結點,因此L要取&
{
	ListNode *tmp = new ListNode;
	tmp->m_value = value;
	if (L == nullptr || pos == 0)
	{
		tmp->m_pNext = L;
		L = tmp;
		return;
	}

	if ( pos >= ListLen(L) || pos < 0)
	{
		cout << "Error input pos."<< endl;
		return;
	}

	ListNode *pInsert = L;
	while (--pos)
	{
		pInsert = pInsert->m_pNext;
	}
	tmp->m_pNext = pInsert->m_pNext;
	pInsert->m_pNext = tmp;

	return;
}
// -----------------在pos後插入--------------------
void InsertListNodeFromBack(ListNode *&L, int pos, int value)
{
	ListNode *tmp = new ListNode;
	tmp->m_value = value;
	if (L == nullptr || pos == 0)
	{
		tmp->m_pNext = L;
		L = tmp;
		return;
	}
	
	if ( pos >= ListLen(L) || pos < 0)
	{
		cout << "Error input pos."<< endl;
		return;
	}

	ListNode *pInsert = L;
	while (pos--)
	{
		pInsert = pInsert->m_pNext;
	}
	tmp->m_pNext = pInsert->m_pNext;
	pInsert->m_pNext = tmp;

	return;
}

5. 刪除。

void DeleteListNode(ListNode *&L, int value)
{
	if (L == nullptr)
	{
		cout << "Error input L.";
		return;
	}

	ListNode *pDelete = L;
	if (L->m_value == value)
	{
		L = L->m_pNext;
		delete pDelete;
		return;
	}
	
	ListNode *pFront = nullptr;
	while (pDelete != nullptr && pDelete->m_value != value)
	{
		pFront = pDelete;
		pDelete  = pDelete->m_pNext;
	}

	if (pDelete == nullptr)
	{
		cout << "Not found value in L.";
		return;
	}
	if (pDelete->m_value == value)
	{
		pFront->m_pNext = pDelete->m_pNext;
		delete pDelete;
	}

	return;
}

6. 翻轉。

ListNode * ReverseList(ListNode *L)
{
	if(L == nullptr || L->m_pNext == nullptr)
		return nullptr;

	ListNode * pCurrent = nullptr;

	while (L != nullptr)
	{
		ListNode * tmp = L->m_pNext;
		L->m_pNext = pCurrent;
		pCurrent = L;
		L = tmp;
	}

	/*DisplayList(L);
	DisplayList(pCurrent);*/
	return pCurrent;
}

7. 列印。

void DisplayList(ListNode * L)
{
	ListNode * list = L;

	while (list != nullptr)
	{
		cout << list->m_value << " -> ";
		list = list->m_pNext;
	}
	cout << "null" << endl;
}

8. 連結串列長度。

int ListLen(ListNode *L)
{
	int len = 0;

	while (L != nullptr)
	{
		L = L->m_pNext;
		++len;
	}

	return len;
}

9. 找到連結串列倒數第k個節點。

ListNode *FindKthNode(ListNode *L, int k)
{
	if (ListLen(L) < k)
		return nullptr;

	ListNode *pFront = nullptr, *pBack = nullptr;

	pFront = pBack = L;

	int ii = 0;
	while (ii++ < k-1)
	{
		pFront = pFront->m_pNext;
	}

	while (pFront->m_pNext != nullptr)
	{
		pFront = pFront->m_pNext;
		pBack = pBack->m_pNext;
	}

	return pBack;
}

10. 合併兩排序的連結串列。

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
        if (pHead1 == nullptr)
		  return pHead2;
	  if (pHead2 == nullptr)
		  return pHead1;

	  ListNode *pNewHead = nullptr, *pNewTail = nullptr;
	  ListNode *pNode1 = pHead1, *pNode2 = pHead2;

	  while (pNode1 != nullptr && pNode2 != nullptr)
	  {
		ListNode *pTmp = nullptr;

		if (pNode1->val < pNode2->val)
		{
			pTmp = pNode1;
			pNode1 = pNode1->next;
		}
		else
		{
			pTmp = pNode2;
			pNode2 = pNode2->next;
		}

		if (pNewHead == nullptr)
		{
			pNewHead = pNewTail = pTmp;
		}
		else
		{
			pNewTail->next = pTmp;
			pNewTail = pNewTail->next;
		}
	  }

	  if ( pNode1 == nullptr )  //和歸併排序時的判斷條件不一樣。
	  {
		pNewTail->next = pNode2;
	  }
	  if ( pNode2 == nullptr )
	  {
		pNewTail->next  = pNode1;
	  }

	  return pNewHead;
}

11. 刪除連結串列中重複的節點. 180420

ListNode* deleteDuplication(ListNode* pHead)
{
	if(pHead == nullptr)
		return nullptr;
	if (pHead->next == nullptr)
		return pHead;
	if (pHead->val == pHead->next->val && pHead->next->next == nullptr)
		return nullptr;

	ListNode *pNewNode = new ListNode(-1);
	pNewNode->next = pHead;
	
	ListNode *pLastNode = pNewNode;
	ListNode *pCurNode = pHead;
	ListNode *pNextNode = pHead->next;
	int flag = 0;

	while (pNextNode != nullptr)
	{
		while (pNextNode && pCurNode->val == pNextNode->val)
		{
			pNextNode = pNextNode->next;
			pCurNode->next = pNextNode;
			flag = 1;
		}
		
		if ( flag )
		{
			pCurNode = pNextNode;
			pLastNode->next = pCurNode;
			flag = 0;

			if (pNextNode)
				pNextNode = pNextNode->next;
		}

		if (pNextNode && pCurNode->val != pNextNode->val)
		{
			pLastNode = pLastNode->next;
			pCurNode = pCurNode->next;
			pNextNode = pNextNode->next;
		}

	}

	return pNewNode->next;
}

12. 找兩個連結串列第一個公共節點。 180424

struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2)
{
      if (pHead1 == nullptr || pHead2 == nullptr)
      return nullptr;
        
      ListNode *pNode1 = pHead1, *pNode2 = pHead2;
	int cnt1 = 0, cnt2 = 0;
	while (pNode1)
	{
		cnt1++;
		pNode1 = pNode1->next;
	}
	while (pNode2)
	{
		cnt2++;
		pNode2 = pNode2->next;
	}

	pNode1 = (cnt1>cnt2) ? pHead1 : pHead2;
	pNode2 = (cnt1>cnt2) ? pHead2 : pHead1;

	for (int ii=0; ii!=abs(cnt1-cnt2); ++ii)
		pNode1 = pNode1->next;

	ListNode * pRet = nullptr;
	while (pNode1 && pNode2)
	{
		if (pNode1 == pNode2)
		{
			pRet = pNode1;
			break;
		}
		else
		{
			pNode1 = pNode1->next;
			pNode2 = pNode2->next;
		}
	}

	return pRet;
}

13. 複雜連結串列的複製。180424

struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
// ---------------牛客網大神通過的版本---------------
RandomListNode* Clone(RandomListNode* pHead )
{
	if(!pHead)
		return NULL;
	RandomListNode *currNode = pHead;

	while(currNode)
	{
		RandomListNode *node = new RandomListNode(currNode->label);
		node->next = currNode->next;
		currNode->next = node;
		currNode = node->next;
	}
	currNode = pHead;
	while(currNode)
	{
		RandomListNode *node = currNode->next;
		if (currNode->random)
			node->random = currNode->random->next;

		currNode = node->next;
	}
	//拆分
	RandomListNode *pCloneHead = pHead->next;
	RandomListNode *tmp;
	currNode = pHead;

	while(currNode->next)
	{
		tmp = currNode->next;
		currNode->next =tmp->next;
		currNode = tmp;
	}

	return pCloneHead;
}
// ------------ 自己的版本,未通過,錯誤尚未找出-------------
RandomListNode* Clone(RandomListNode* pHead)
{
        if (pHead == nullptr)
		  return nullptr;

	  // ------------ 拷貝----------------
	  RandomListNode *pNode = pHead;
	  while (pNode)
	  {
		RandomListNode *pTmp = new RandomListNode(*pNode);
		pTmp->next = pNode->next;
		pNode->next = pTmp;

		if (pNode->random)
			pTmp->random = pNode->random->next;

		pNode = pTmp->next;
		pTmp = nullptr;
	  }

	  // ------------- 拆分-----------------
	  pNode = pHead;
	  RandomListNode *pNewHead = pHead->next;
	  RandomListNode *pProcNode = pNewHead;

	  if (pNode)   // 要讓pNode走在pProcNode的前面。
	  {
		pNode->next = pProcNode->next;
		pNode = pNode->next;
	  }

	  while (pProcNode && pNode)
	  {
		pProcNode->next = pNode->next;  // pNode後面不會是空,所以這樣賦值可以確保pProcNode後面不會是空
		pProcNode = pProcNode->next;
		pNode->next = pProcNode->next;
		pNode = pNode->next;
	  }

	  return pNewHead;
}

14. 尋找連結串列中環的入口節點。180425

ListNode* EntryNodeOfLoop(ListNode* pHead)
{
	if(pHead == nullptr || pHead->next == nullptr)
		return nullptr;

	ListNode *pFast = pHead, *pLow = pHead;

	// ----------- 獲取環的大小------------
	int cnt = 0;
	pFast = pFast->next;
	pFast = (pFast==nullptr) ? pFast : pFast->next;
	pLow = pLow->next;
	++cnt;

	while (pFast != pLow && pFast)
	{
		pFast = pFast->next;
		pFast = (pFast==nullptr) ? pFast : pFast->next;
		pLow = pLow->next;
		++cnt;
	}

	if (pFast == nullptr)
		return nullptr;

	// --------獲取環的入口-----------
	pFast = pLow = pHead;
	ListNode *pRet = nullptr;
	for (int ii=0; ii != cnt; ++ii)
		pFast = pFast->next;
	
	while (pFast != pLow)
	{
		pFast = pFast->next;
		pLow = pLow->next;
	}
	pRet = pFast;

	return pRet;
	
}

15. 主函式測試。

int main(int argc, char **argv)
{
	ListNode * L = new ListNode;
	InitList(L);

	cout << "請輸入連結串列節點,以-1結束:" << endl;
	L = CreateListFromTail();
	DisplayList(L);

	int pos = 1, value = 10;
	cout << "在指定位置 " << pos << " 前方插入節點:" << value << endl;
	InsertListNodeFromFront(L, pos, value);
	DisplayList(L);

	pos = 2; value = 20;
	cout << "在指定位置 " << pos << " 後方插入節點:" << value << endl;
	InsertListNodeFromBack(L, pos, value);
	DisplayList(L);

	cout << "刪除值為 " << value << " 的節點:"  << endl;
	DeleteListNode(L, value);
	DisplayList(L);

	cout << "翻轉連結串列:" << endl;
	ListNode *R = ReverseList(L);
	DisplayList(R);

	getchar();getchar();
	return 0;
}

測試結果: