c++鏈表問題彙總

目錄

一、鏈表與指針

1.1 單向雙向表

1.2 數組指針

1.3 鏈表的合併

二、鏈表應用匯總

2.1 鏈表中的值倒序

2.2 鏈表的倒數第k個節點

2.3 反轉鏈表

2.4 鏈表的公共節點

2.5 鏈表環的入口節點

三、複雜鏈表的複製

3.1 題幹

3.2 思路及注意事項

3.3 正確答案

3.4 錯誤代碼找錯

四、二叉樹轉爲雙向鏈表


相應oj鏈接:

https://www.nowcoder.com/ta/coding-interviews

一、鏈表與指針

1.1 單向雙向表

設某鏈表中最常用的操作是在鏈表的尾部插入或刪除元素,則選用下列(  )存儲方式最節省運算時間

正確答案: D

  • 單向鏈表
  • 單向循環鏈表
  • 雙向鏈表
  • 雙向循環鏈表

迅速從表頭到表尾,單向鏈表於單向循環鏈表顯然不行,雙向不循環也不行,最快還是雙向循環。

1.2 數組指針

下面代碼會輸出  ?。

    int a[4] = { 1, 2, 3, 4 };
    cout << sizeof(a) << endl;
    int *ptr = (int*)(&a + 1);
    printf("%d", *(ptr - 1));

解析:

  • &a+1是整個數組的指向棏下一個地址,
  • ptr-1 就是從下一個地址(隨機地址)回到數組的最後一個數存儲的地方
  • &a + 1: 取數組a 的首地址,該地址的值加上sizeof(a) 的值,即&a + 4*sizeof(int),也就是下一個數組的首地址,顯然當前指針已經越過了數組的界限。
  • (int *)(&a+1): 則是把上一步計算出來的地址,強制轉換爲int * 類型,賦值給ptr。
  • *(a+1): a,&a 的值是一樣的,但意思不一樣,a 是數組首元素的首地址,也就是a[0]的首地址,&a 是數組的首地址,a+1 是數組下一元素的首地址,即a[1]的首地址,&a+1 是下一個數組的首地址。所以輸出2
  • *(ptr-1): 因爲ptr 是指向a[3],並且ptr 是int * 類型,所以*(ptr-1) 是指向a[3] ,

經過在編譯器編譯之後,輸出爲

16
4

1.3 鏈表的合併

已知兩個鏈表list1 和list2 內的數據都是有序的,請把它們合併成一個鏈表,保持裏面的數據依然有序,要求用遞歸的方法實現()。下面是定義的鏈表的節點:

struct Node {
	int data;
	Node *next;
};
typedef struct Node Node;

請寫出函數Node * MergeRecursive(Node *head1, Node *head2)的實現。

解析:運用遞歸實現了鏈表的合併,迴歸表頭,其他部分對更小的值進行遞歸。

程序作用就是,

  • 如果兩個head指向的都有值,則返回指向值小的head,這個head指向的值往下遞歸
  • 如果一個head爲NULL,則返回另一個head。這部分適用於程序結束時候的情況。即出現這種情況遞歸就結束了。
Node * MergeRecursive(Node *head1, Node *head2)
{
	if (head1 == NULL)
		return head2;
	if (head2 == NULL)
		return head1;
	Node *head = NULL;
	if (head1->data < head2->data){
		head = head1;
		head->next = MergeRecursive(head1->next, head2);
	}
	else {
		head = head2;
		head->next = MergeRecursive(head1, head2->next);
	}
	return head;
}

 

二、鏈表應用匯總

2.1 鏈表中的值倒序


/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        ListNode* node_ptr=head;
        vector<int> val_vector;
        for( ; node_ptr!=NULL; node_ptr=node_ptr->next){
            val_vector.insert(val_vector.begin(),node_ptr->val);
        }
        // reverse vector
        return val_vector;
    }
};

難度不難,但是需要對vector這個工具進行熟練運用。

比如insert命令是從前面插入。

然後就是鏈表的遍歷。

2.2 鏈表的倒數第k個節點

輸入一個鏈表,輸出該鏈表中倒數第k個結點。

倒數第k個節點c++

思路清晰即可

/*
struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) :
                        val(x), next(NULL) {
        }
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(pListHead==NULL)return NULL;
        int list_size=0;
        ListNode* node_ptr=pListHead;
        while(node_ptr!=NULL){
            node_ptr=node_ptr->next;
            list_size++;
        }
        int idx_back_k=list_size-k;
        if(idx_back_k<0)return NULL;
        node_ptr=pListHead;
        for(int idx=0;idx<idx_back_k;idx++){
            node_ptr=node_ptr->next;
        }
        return node_ptr;
    }
};

 可以採用劍指offer中的快慢指針來解,即快指針指向慢指針之後的k位,一起向後遍歷,快指針指向NULL的時候,慢指針剛好在倒數第k個節點。

另外,需要注意程序魯棒性,即鏈表不夠k位的情況。這種方法應該是最簡單的方法了。

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        ListNode* fast_ptr=pListHead;
        ListNode* slow_ptr=pListHead;
        for(int idx=0;idx<k;idx++){
            if(fast_ptr==NULL)return NULL;
            fast_ptr=fast_ptr->next;
        }
        while(fast_ptr!=NULL){
            fast_ptr=fast_ptr->next;
            slow_ptr=slow_ptr->next;
        }
        return slow_ptr;
    }
};

2.3 反轉鏈表

反轉鏈表需要將之前的鏈表進行反轉。因爲內存限制,c++代碼最好不要引入新的變量。數據結構的題,有必要畫出相應的圖方便不出錯與理解。

一定要考慮程序魯棒性,即如果空鏈表的話,返回NULL,不要直接return。

並且需要少量的內存佔用,儘量運用已有的節點。

/*
struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) :
                        val(x), next(NULL) {
        }
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead==NULL)return NULL;
        ListNode* pre_node=new ListNode(pHead->val);
        ListNode* cur_node=pHead->next;
        ListNode* next_node=cur_node;
        while(next_node!=NULL){
            next_node=cur_node->next;
            cur_node->next=pre_node;
            pre_node=cur_node;
            cur_node=next_node;
        }
        return pre_node;
    }
};

2.4 鏈表的公共節點

找到兩個鏈表公共節點,思路清晰即可,注意非void函數一定要返回值。且第一個鏈表固定一個節點時候,第二個鏈表需要從頭開始。兩次遍歷,找到公共節點。不知是否有更簡便的方法。

/*
struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) :
                        val(x), next(NULL) {
        }
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if(pHead1==NULL||pHead2==NULL)return NULL;
        ListNode* node_1=pHead1;
        ListNode* node_2;
        while(node_1!=NULL){
            node_2=pHead2;
            while(node_2!=NULL){
                if(node_1==node_2)return node_1;
                node_2=node_2->next;
            }
            node_1=node_1->next;
        }
        return NULL;
    }
};

2.5 鏈表環的入口節點

鏈表其中包含環,如果有環則輸出入口節點,沒有則輸出NULL

需要一快一慢兩個指針,相遇則表明有環。可以通過環中的節點判斷環的大小。

class Solution {
public:
        // fast ptr and slow ptr find meeting node
        ListNode* meeting_node(ListNode* pHead){
                if (pHead == NULL)return NULL;
                ListNode* fast_ptr = pHead->next;
                ListNode* slow_ptr = pHead;
                while (fast_ptr != slow_ptr){
                        if (fast_ptr == NULL)return NULL;
                        fast_ptr = fast_ptr->next;
                        if (fast_ptr == NULL)return NULL;
                        fast_ptr = fast_ptr->next;
                        slow_ptr = slow_ptr->next;
                }
                return fast_ptr;
        }
// entry node
        ListNode* EntryNodeOfLoop(ListNode* pHead)
        {
                ListNode* meet_node_ptr = meeting_node(pHead);
                if (meet_node_ptr == NULL)return NULL;
ListNode* fast_ptr = meet_node_ptr->next;
                ListNode* entry_node = pHead->next;
                while (fast_ptr != meet_node_ptr){
                        fast_ptr = fast_ptr->next;
                        entry_node = entry_node->next;
                }
fast_ptr = pHead;
                while (fast_ptr != entry_node){
                        fast_ptr = fast_ptr->next;
                        entry_node = entry_node->next;
                }
                return entry_node;
        }
};

 

三、複雜鏈表的複製

OJ:https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba?tpId=13&tqId=11178&tPage=2&rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

3.1 題幹

輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)

3.2 思路及注意事項

首先,把新鏈表複製到舊鏈表的結尾

然後,複製randm指針(注意,random指針可能爲NULL的情況出現)

最後,把鏈表拆開。

3.3 正確答案

分開用子函數實現:

class Solution {
public:
	//void CloneNodes(RandomListNode *);
	//void ConnectRandomNodes(RandomListNode* );
	//RandomListNode* ReconnectNodes(RandomListNode* );
	RandomListNode* Clone(RandomListNode* pHead)
	{
		if (pHead == nullptr)
			return NULL;
		CloneNodes(pHead);
		ConnectRandomNodes(pHead);
		return ReconnectNodes(pHead);

	}
	void CloneNodes(RandomListNode *Head)
	{
		RandomListNode* pNode = Head;
		while (pNode != nullptr)
		{
			RandomListNode* pCloned = new RandomListNode(pNode->label);
			//pCloned->label=pNode->label;
			pCloned->next = pNode->next;
			pCloned->random = nullptr;

			pNode->next = pCloned;
			pNode = pCloned->next;
		}
	}
	void ConnectRandomNodes(RandomListNode* Head)
	{
		RandomListNode* pNode = Head;
		while (pNode != nullptr)
		{
			RandomListNode* pCloned = pNode->next;
			if (pNode->random != nullptr)
			{
				pCloned->random = pNode->random->next;
			}
			pNode = pCloned->next;
		}
	}
	RandomListNode* ReconnectNodes(RandomListNode* Head)
	{
		RandomListNode *pNode = Head;
		RandomListNode *result = Head->next;
		while (pNode != NULL)
		{
			RandomListNode *pClone = pNode->next;
			pNode->next = pClone->next;
			pNode = pNode->next;
			if (pNode != NULL)
				pClone->next = pNode->next;

		}
		return result;
	}

};

合併實現:

class Solution {
public:
	RandomListNode* Clone(RandomListNode* pHead)
	{
		if (!pHead) return NULL;
		RandomListNode *cur = pHead;
		while (cur){
			RandomListNode *node = new RandomListNode(cur->label);
			node->next = cur->next;
			cur->next = node;
			cur = node->next;
		}//直到cur指向了原先鏈表的結尾null處
		cur = pHead;
		RandomListNode *p;
		while (cur){
			p = cur->next;
			if (cur->random){
				p->random = cur->random->next;

			}
			cur = p->next;
		}

		RandomListNode *pCloneHead = pHead->next;
		RandomListNode *tmp;
		cur = pHead;
		while (cur->next){
			tmp = cur->next;
			cur->next = tmp->next;
			cur = tmp;
		}
		return pCloneHead;
	}
};

個人編寫正確的答案

class Solution {
public:
	void simple_clone(RandomListNode* pHead){
		RandomListNode* old_ptr = pHead;
		while (old_ptr != NULL){
			RandomListNode* new_ptr = new RandomListNode(old_ptr->label);
			new_ptr->next = old_ptr->next;
			old_ptr->next = new_ptr;
			old_ptr = new_ptr->next;
		}
	}
	void clone_random(RandomListNode* pHead){
		RandomListNode* old_ptr = pHead;
		while (old_ptr != NULL){
			RandomListNode* new_ptr = old_ptr->next;
			if (old_ptr->random != NULL){
				new_ptr->random = old_ptr->random->next;
			}
			old_ptr = new_ptr->next;
		}
	}
	RandomListNode* split(RandomListNode* pHead){
		RandomListNode* old_ptr = pHead;
		RandomListNode* new_head = pHead->next;
		RandomListNode* new_ptr = new_head;
		while (new_ptr->next != NULL){
			old_ptr->next = new_ptr->next;
			old_ptr = old_ptr->next;
			new_ptr->next = old_ptr->next;
			new_ptr = new_ptr->next;
		}
		new_ptr->next = NULL;
		old_ptr->next = NULL;
		return new_head;
	}

	RandomListNode* Clone(RandomListNode* pHead)
	{
		if (pHead == NULL)return NULL;
		//simple clone
		simple_clone(pHead);
		clone_random(pHead);
		return split(pHead);
	}
};

3.4 錯誤代碼找錯

下面代碼找錯誤:提醒,用oj會出現堆棧溢出

#include<vector>
#include<iostream>
using namespace std;


struct RandomListNode {
	int label;
	struct RandomListNode *next, *random;
	RandomListNode(int x) :
		label(x), next(NULL), random(NULL) {
	}
};

class Solution {
public:
	void simple_append_clone(RandomListNode* pHead){
		if (pHead == NULL)return;
		RandomListNode* old_node = pHead;

		while (old_node != NULL){
			RandomListNode* new_node = new RandomListNode(old_node->label);
			new_node->next = old_node->next;
			old_node->next = new_node;
			old_node = new_node->next;
		}
	}

	void clone_random(RandomListNode* pHead){
		if (pHead == NULL)return;
		RandomListNode* old_node = pHead;

		while (old_node != NULL){
			if (old_node->random != NULL)old_node->next->random = old_node->random->next;
			old_node = old_node->next->next;
		}
	}

	RandomListNode* divide(RandomListNode* pHead){
		if (pHead == NULL)return NULL;
		RandomListNode* new_head = pHead->next;
		RandomListNode* new_node = new_head;
		RandomListNode* old_node = pHead;
		while (new_node->next != NULL){
			new_node = old_node->next;
			old_node->next = old_node->next->next;
			old_node = old_node->next;
			if (new_node->next != NULL){
				new_node->next = new_node->next->next;
				new_node = new_node->next;
			}
		}
		return new_head;
	}

	RandomListNode* Clone(RandomListNode* pHead)
	{
		if (pHead == NULL)return NULL;
		simple_append_clone(pHead);
		clone_random(pHead);
		return divide(pHead);
	}
};

int main(){
	Solution Solution;
	vector<int> rating;
	int num; cin >> num;
	while (num--){
		int rate; cin >> rate;
		rating.push_back(rate);
	}

	int end; cin >> end;
	return 0;
}

改正方法:把第三個函數最終的判斷刪掉,加上兩個指針都指向NULL,表示到了結尾

四、二叉樹轉爲雙向鏈表

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

中序遍歷即可,多一個記錄pre的指針

class Solution {
public:
	TreeNode* Convert(TreeNode* pRootOfTree)
	{
		if (pRootOfTree == nullptr) return nullptr;
		TreeNode* pre = nullptr;

		convertHelper(pRootOfTree, pre);

		TreeNode* res = pRootOfTree;
		while (res->left)
			res = res->left;
		return res;
	}

	void convertHelper(TreeNode* cur, TreeNode*& pre)
	{
		if (cur == nullptr) return;
		convertHelper(cur->left, pre);

		cur->left = pre;
		if (pre) pre->right = cur;
		pre = cur;
		convertHelper(cur->right, pre);
	}
};




來自 <https://www.nowcoder.com/profile/3900945/codeBookDetail?submissionId=16370979>
 

或者:
class Solution {
public:
    void ConvertNode(TreeNode* pNode, TreeNode** pLastNodeInList){
        if (pNode == nullptr)
            return;

        //if(pNode->left!=nullptr)
        ConvertNode(pNode->left, pLastNodeInList);

        pNode->left = *pLastNodeInList;
        if (*pLastNodeInList != nullptr)
            (*pLastNodeInList)->right = pNode;
        *pLastNodeInList = pNode;

        //if(pNode->right!=nullptr)
        ConvertNode(pNode->right, pLastNodeInList);
    }

    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        TreeNode* pLastNodeInList = nullptr;
        ConvertNode(pRootOfTree, &pLastNodeInList);

        TreeNode* pHeadOfList = pLastNodeInList;
        while (pHeadOfList != nullptr&&pHeadOfList->left != nullptr){
            pHeadOfList = pHeadOfList->left;
        }

        return pHeadOfList;
    }
};

來自 <https://www.nowcoder.com/profile/349656/codeBookDetail?submissionId=17180119>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章