链表问题合集

判断一个链表是否为回文结构

题目

给定一个链表的头节点head,请判断该链表是否为回文结构;
进阶:如果链表长度为N,要求时间复杂度达到O(N),额外空间复杂度为O(1)

代码

代码部分包含了书中的三种实现方法,如下

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

struct node {
	int value;
	node* next;
	node(int val)
	{
		value = val;
	}
};

node* createList(int* in, int len)
{
	node* head = new node(in[0]);
	node* p = head;
	for (int i = 1; i < len; i++)
	{
		node* pNode = new node(in[i]);
		p->next = pNode;
		p = p->next;
	}
	p->next = NULL;
	return head;
}

void printList(node* head)
{
	if (head == NULL)
		cout << "NULL" << endl;
	cout << "Whole list:" << endl;
	while (head != NULL)
	{
		cout << head->value << " ";
		head = head->next;
	}
	cout << endl;
}
/*一种解法是将链表反转,但是这样会改变链表原始结构,
若将链表另存,那么再将其反转,则时间复杂度会到O(n);
或者使用栈将链表中的节点依次压入,再逐渐弹出时逐个比较,如果相同则为回文结构*/
bool isPalindrome1(node* head)
{
	bool res = true;
	if (head == NULL || head->next == NULL)
		return res;
	stack<node*> s;
	node* cur = head;
	while (cur != NULL)
	{
		s.push(cur);
		cur = cur->next;
	}
	cur = head;
	while (!s.empty())
	{
		if (cur->value != s.top()->value)
		{
			res = false;
			break;
		}
		cur = cur->next;
		s.pop();
	}

	return res;
}
/*另一种同样适用栈的方法是只将链表的右半部份压入栈中,此时对比
链表前半部分和后半部分的节点,如果节点相同,那么是回文结构,否则
不是回文结构;空间复杂度同样为O(n)*/
bool isPalindrome2(node* head)
{
	if (head == NULL || head->next == NULL)
		return true;
	node* cur = head;
	node* right = head->next;
	while (cur->next != NULL && cur->next->next != NULL)
	{
		right = right->next;
		cur = cur->next->next;
	}
	stack<node*> s;
	while (right != NULL)
	{
		s.push(right);
		right = right->next;
	}
	while (!s.empty())
	{
		if (head->value != s.top()->value)
		{
			return false;
		}
		s.pop();
		head = head->next;
	}
	return true;
}

/*第三种方法,将后半部分指针反向,会改变链表的整体结构,但是额外空间
复杂度为O(1)*/
bool isParlindrome3(node* head)
{
	if (head == NULL || head->next == NULL)
		return true;
	node* n1 = head;
	node* n2 = head;
	while (n2->next != NULL && n2->next->next != NULL)
	{
		n1 = n1->next;
		n2 = n2->next->next;
	}
	n2 = n1->next;
	n1->next = NULL;
	node* n3 = NULL;
	while (n2 != NULL)
	{
		n3 = n2->next;
		n2->next = n1;
		n1 = n2;
		n2 = n3;
	}
	n3 = n1;
	n2 = head;
	bool res = true;
	while (n1 != NULL && n2 != NULL)
	{
		if (n1->value != n2->value)
		{
			res = false;
			break;
		}
		n1 = n1->next;
		n2 = n2->next;
	}
	n1 = n3->next;
	n3->next = NULL;
	while (n1 != NULL) //在最后进行链表恢复,恢复到原来的结构
	{
		n2 = n1->next;
		n1->next = n3;
		n3 = n1;
		n1 = n2;
	}
	return res;
}

int main()
{
	int input[] = { 1, 2, 3, 3, 2, 1 };
	int len = 6;
	node* p1 = createList(input, len);
	node* p2 = createList(input, len);
	node* p3 = createList(input, len);
	bool res1 = isPalindrome1(p1);
	bool res2 = isPalindrome2(p2);
	bool res3 = isParlindrome3(p3);
	cout << res1 << res2 << res3 << endl;
	getchar();
	return 0;
}

复制含有随机指针节点的链表

题目

具体题目请参考原书籍;
进阶:不适用额外数据结构,只用有限几个变量,且在时间复杂度*O(N)*内完成原问题要实现的函数

代码

以下代码值包含了两种解决方法,不包含测试用例

struct randomNode {
	int value;
	randomNode* next;
	randomNode* random;
	randomNode (int val)
	{
		value = val;
	}
};

/*方法1:使用map*/
randomNode* copyListWithRand1(randomNode* head)
{
	map<randomNode*, randomNode*> mp;
	randomNode* cur = head;
	while (cur != NULL)
	{
		mp[cur] = new randomNode(cur->value);
		cur = cur->next;
	}
	cur = head;
	while (cur != NULL)
	{
		mp[cur]->next = mp[cur->next];
		mp[cur]->random = mp[cur->random];
		cur = cur->next;
	}
	return mp[head];
}

/*方法二:*/
randomNode* copyListWithRand2(randomNode* head)
{
	if (head == NULL)
		return NULL;
	randomNode* cur = head;
	randomNode* next = NULL;
	while (cur != NULL)
	{
		next = cur->next;
		cur->next = new randomNode(cur->value);
		cur->next->next = next;
		cur = next;
	}
	cur = head;
	randomNode* curCopy = NULL;
	while (cur != NULL)
	{
		next = cur->next->next;
		curCopy = cur->next;
		curCopy->random = cur->random != NULL ? cur->random->next : NULL;
		cur = next;
	}
	randomNode* res = head->next;
	cur = head;
	//拆分链表
	while (cur != NULL)
	{
		next = cur->next->next;
		curCopy = cur->next;
		cur->next = next;
		curCopy->next = next != NULL ? next->next : NULL;
		cur = next;
	}
	return res;
}

两个链表生成相加链表

题目

假设链表中每个节点的值都在0~9之间,那么链表整体可以代表一个整数。给定两个这种链表的头节点head1和head2,生成代表两个整数相加值的结果链表。
例如:链表1为9->3->7,链表2为6->3,最后生成新的结果链表尾1->0->0->0;

代码

/*一:如果将对应链表转换成相应的数组再进行相加,可能会有整数溢出的情况*/
/*二:利用栈求解,将数字分别依次压入栈中,随后在弹出的过程中依次相加
并记录是否存在进位,如果有进位,则生成一个value为0的节点*/
#include<iostream>
#include<stack>
using namespace std;

struct node {
	int value;
	node* next;
	node(int val)
	{
		value = val;
	}
};

node* createList(int* in, int len)
{
	node* head = new node(in[0]);
	node* p = head;
	for (int i = 1; i < len; i++)
	{
		node* pNode = new node(in[i]);
		p->next = pNode;
		p = p->next;
	}
	p->next = NULL;
	return head;
}

void printList(node* head)
{
	if (head == NULL)
		cout << "List NULL" << endl;
	node* cur = head;
	cout << "The whole list:" << endl;
	while (cur != NULL)
	{
		cout << cur->value << " ";
		cur = cur->next;
	}
	cout << endl;
}

node* addList(node* head1, node* head2)
{
	stack<node*> list1;
	stack<node*> list2;
	while (head1 != NULL)
	{
		list1.push(head1);
		head1 = head1->next;
	}
	while (head2 != NULL)
	{
		list2.push(head2);
		head2 = head2->next;
	}
	int ca = 0;
	int n1 = 0;
	int n2 = 0;
	int n = 0;
	node* pNode = NULL;
	node* pre = NULL;
	while (!list1.empty() || !list2.empty())
	{
		n1 = list1.empty() ? 0 : list1.top()->value;
		n2 = list2.empty() ? 0 : list2.top()->value;
		if (!list1.empty())
			list1.pop();
		if (!list2.empty())
			list2.pop();
		n = n1 + n2 + ca;
		ca = n / 10;
		pre = pNode;
		pNode = new node(n % 10);
		pNode->next = pre;
	}
	if (ca == 1)
	{
		pre = pNode;
		pNode = new node(1);
		pNode->next = pre;
	}
	return pNode;
}

/*方法二:将两个链表逆序,可省去栈占用的空间;计算完成之后,再次对原链表
逆序,调整会原样*/
node* reverseList(node* head)
{
	if (head == NULL || head->next == NULL)
		return head;
	node* pre = NULL;
	node* next = NULL;
	while (head != NULL)
	{
		next = head->next;
		head->next = pre;
		pre = head;
		head = next;
	}
	return pre;
}

node* addList2(node* head1, node* head2)
{
	head1 = reverseList(head1);
	head2 = reverseList(head2);
	int ca = 0;
	int n1 = 0;
	int n2 = 0;
	int n = 0;
	node* pHead1 = head1;
	node* pHead2 = head2;
	node* pNode = NULL;
	node* pre = NULL;
	while (pHead1 != NULL || pHead2 != NULL)
	{
		n1 = pHead1 != NULL ? pHead1->value : 0;
		n2 = pHead2 != NULL ? pHead2->value : 0;
		n = n1 + n2 + ca;
		pre = pNode;
		pNode = new node(n % 10);
		pNode->next = pre;
		ca = n / 10;
		pHead1 = pHead1 != NULL ? pHead1->next : NULL;
		pHead2 = pHead2 != NULL ? pHead2->next : NULL;
	}
	if (ca == 1)
	{
		pre = pNode;
		pNode = new node(1);
		pNode->next = pre;
	}
	reverseList(head1);
	reverseList(head2);
	return pNode;
}

int main()
{
	int input1[] = { 9, 3, 7 };
	int len1 = 3;
	int input2[] = { 6, 3 };
	int len2 = 2;
	node* head1 = createList(input1, len1);
	node* head2 = createList(input2, len2);
	node* newHead = addList(head1, head2);
	node* newHead1 = addList2(head1, head2);
	printList(newHead);
	printList(newHead1);
	getchar();
	return 0;
}

两个单链表相交的一系列问题

题目

本题中,单链表可能有环,也可能无环。给定两个单链表的头节点head1和head2,这两个链表可能相交,也可能不相交。实现一个函数,如果两个链表相交,返回相交的第一个节点;如果不相交,返回NULL。

要求

如果链表1长度为N,链表2长度为M,时间复杂度达到O(N+M),额外空间复杂度达到O(1)

解题思路

  • 如何判断一个链表是否有环,如果有,则返回第一个进入环的节点,如果没有则返回NULL。
  • 如何判断两个无环链表是否相交,相交则返回第一个相交节点,不相交则返回NULL。
  • 如何判断两个有环链表是否相交,相交则返回第一个相交节点,不相交则返回NULL。
    各问题详细解答请参考原书。

代码

具体测试用例还需要再链表中设置环,由于比较懒,于是没有实际的测试用例,只有三个对应的函数。

#include<iostream>
#include<stack>
#include<algorithm>
using namespace std;
struct node {
	int value;
	node* next;
	node(int val)
	{
		value = val;
	}
};

/*1、判断一个链表是否有环,若有,返回环的入口点*/
node* entryOfListWithCircle(node* head)
{
	if (head == NULL || head->next == NULL || head->next->next == NULL)
		return NULL;
	node* first = head->next;
	node* second = head->next->next;
	while (first != second)
	{
		if (second->next == NULL || second->next->next == NULL)
			return NULL;
		first = first->next;
		second = second->next->next;
	}
	first = head;
	while (first != second)
	{
		first = first->next;
		second = second->next;
	}
	return first;
}

/*2、两个链表无环但相交,找出交点*/
node* commonNodeOfTwoList(node* head1, node* head2)
{
	if (head1 == NULL || head2 == NULL)
		return NULL;
	node* cur1 = head1;
	node* cur2 = head2;
	int n = 0;
	while (cur1->next != NULL)
	{
		n++;
		cur1 = cur1->next;
	}
	while (cur2->next != NULL)
	{
		n--;
		cur2 = cur2->next;
	}
	if (cur1 != cur2)
		return NULL;
	cur1 = n > 0 ? head1 : head2;
	cur2 = cur1 == head1 ? head2 : head1;
	n = abs(n);
	while (n != 0)
	{
		cur1 = cur1->next;
		n--;
	}
	while (cur1 != cur2)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	return cur1;
}

/*3、两个链表有环且相交,找出交点*/
node* bothLoop(node* head1, node* loop1, node* head2, node* loop2)
{
	node* cur1 = NULL;
	node* cur2 = NULL;
	if (loop1 == loop2)
	{
		cur1 = head1;
		cur2 = head2;
		int n = 0;
		while (cur1 != loop1)
		{
			n++;
			cur1 = cur1->next;
		}
		while (cur2 != loop2)
		{
			n--;
			cur2 = cur2->next;
		}
		cur1 = n > 0 ? head1 : head2;
		cur2 = cur1 == head1 ? head2 : head1;
		n = abs(n);
		while (n != 0)
		{
			n--;
			cur1 = cur1->next;
		}
		while (cur1 != cur2)
		{
			cur1 = cur1->next;
			cur2 = cur2->next;
		}
		return cur1;
	}
	else
	{
		cur1 = loop1->next;
		while (cur1 != loop1)
		{
			if (cur1 == loop2)
				return loop1;
			cur1 = cur1->next;
		}
		return NULL;
	}
}

将单链表的没K个节点之间逆序

题目

给定一个单链表的头节点head,实现一个调整单链表的函数,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。

代码

根据书中解法得到两种方法,一种使用栈进行逆序,需要的额外空间复杂度为O(N);另外一种方法是在原链表的基础上进行每K个节点之间的逆序,额外空间复杂度为O(1);详细解释请参考原书

#include<iostream>
#include<stack>
using namespace std;
struct node {
	int value;
	node* next;
	node(int val)
	{
		value = val;
	}
};

node* createList(int* in, int len)
{
	node* head = new node(in[0]);
	node* p = head;
	for (int i = 1; i < len; i++)
	{
		node* pNode = new node(in[i]);
		p->next = pNode;
		p = p->next;
	}
	p->next = NULL;
	return head;
}

void printList(node* head)
{
	if (head == NULL)
		cout << "List NULL" << endl;
	node* cur = head;
	cout << "The whole list:" << endl;
	while (cur != NULL)
	{
		cout << cur->value << " ";
		cur = cur->next;
	}
	cout << endl;
}
/*方法1的时间复杂度O(N),空间复杂度O(K);方法二:时间复杂度O(N),空间复杂度O(1)*/
/*方法1:用栈,当栈大小等于K时*/
node* resign1(stack<node*> &s, node* left, node* right)
{
	node* cur = s.top();
	s.pop();
	if (left != NULL)
		left->next = cur;
	node* next = NULL;
	while (!s.empty())
	{
		next = s.top();
		s.pop();
		cur->next = next;
		cur = next;
	}
	cur->next = right;
	return cur;
}

node* reverseKNode1(node* head, int k)
{
	if (k < 2)
		return head;
	stack<node*> s;
	node* newHead = head;
	node* cur = head;
	node* pre = NULL;
	node* next = NULL;
	while (cur != NULL)
	{
		next = cur->next;
		s.push(cur);
		if (s.size() == k)
		{
			pre = resign1(s, pre, next);
			newHead = newHead == head ? cur : newHead;
		}
		cur = next;
	}
	return newHead;
}

/*不需要栈,直接在原链表中调整*/
void resign2(node* left, node* start, node* end, node* right)
{
	node* pre = start;
	node* cur = start->next;
	node* next = NULL;
	while (cur != right)
	{
		next = cur->next;
		cur->next = pre;
		pre = cur;
		cur = next;
	}
	if (left != NULL)
		left->next = end;
	start->next = right;

}

node* reverseKNode2(node* head, int k)
{
	if (k < 2)
		return head;
	node* cur = head;
	node* start = NULL;
	node* pre = NULL;
	node* next = NULL;
	int count = 1;
	while (cur != NULL)
	{
		next = cur->next;
		if (count == k)
		{
			start = pre == NULL ? head : pre->next;
			head = pre == NULL ? cur : head;
			resign2(pre, start, cur, next);
			pre = start;
			count = 0;
		}
		count++;
		cur = next;
	}
	return head;
}

int main()
{
	int input1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
	int input2[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
	int len = 8;
	node* head1 = createList(input1, len);
	node* head2 = createList(input2, len);
	printList(head1);
	printList(head2);
	node* p1 = reverseKNode1(head1, 3);
	node* p2 = reverseKNode2(head2, 3);
	printList(p1);
	printList(p2);
	getchar();
	return 0;
}

删除无序单链表中值重复出现的节点

题目

给定一个无序单链表头节点head,删除其中值重复出现的节点。两种方法实现。
方法1:如果链表长度为N,时间复杂度达到O(N)
方法2:额外空间复杂度为O(1)

代码

  • 使用set使时间复杂度可以达到O(N),空间复杂度为O(N)
  • 使用类似于选择排序的方法,时间复杂度可达到O(N2), 空间复杂度为O(1)
#include<iostream>
#include<map>
#include<set>
using namespace std;
struct node {
	int value;
	node* next;
	node(int val)
	{
		value = val;
	}
};

node* createList(int* in, int len)
{
	node* head = new node(in[0]);
	node* p = head;
	for (int i = 1; i < len; i++)
	{
		node* pNode = new node(in[i]);
		p->next = pNode;
		p = p->next;
	}
	p->next = NULL;
	return head;
}

void printList(node* head)
{
	if (head == NULL)
		cout << "List NULL" << endl;
	node* cur = head;
	cout << "The whole list:" << endl;
	while (cur != NULL)
	{
		cout << cur->value << " ";
		cur = cur->next;
	}
	cout << endl;
}

/*哈希表,空间复杂度为O(n),时间复杂度为O(n)*/
void removeDum(node* head)
{
	if (head == NULL)
		return;
	set<int> noDum;
	node* pre = head;
	node* cur = head->next;
	noDum.insert(head->value);
	while (cur != NULL)
	{
		if (noDum.find(cur->value) != noDum.end())
			pre->next = cur->next;
		else
		{
			noDum.insert(cur->value);
			pre = cur;
		}
		cur = cur->next;
	}
}

/*空间复杂度O(1),时间复杂度为O(n2); 类似选择排序*/
void removeDum2(node* head)
{
	node* cur = head;
	node* pre = NULL;
	node* next = NULL;
	while (cur != NULL)
	{
		pre = cur;
		next = cur->next;
		while (next != NULL)
		{
			if (cur->value == next->value)
				pre->next = next->next;
			else
				pre = next;
			next = next->next;
		}
		cur = cur->next;
	}
}

int main()
{
	int input[] = { 1, 2, 3, 3, 4, 4, 2, 1, 1 };
	int len = 9;
	node* head1 = createList(input, len);
	node* head2 = createList(input, len);
	printList(head1);
	removeDum(head1);
	removeDum2(head2);
	printList(head1);
	printList(head2);
	getchar();
	return 0;
}

将搜索二叉树转换成双向链表

题目

对二叉树的节点来说,有本身的值域和指向左右孩子的两个指针;对双向链表的节点来说,有本身的值域和指向上一个和下一个节点的指针。在结构上两者具有相似性。现在将一颗搜索二叉树转换成一个有序的双向链表。题目及举例请参考原书
以下代码实现了使用队列的方法,以及参考剑指offer的方法,未进行测试。

代码

#include<iostream>
#include<queue>
using namespace std;
struct treeNode {
	int value;
	treeNode* left;
	treeNode* right;
	treeNode(int data)
	{
		value = data;
	}
};

/*1、中序遍历的方法,将对应节点一一存放于一个队列中,之后
依次出对排序*/

void inOrderToQueue(treeNode* head, queue<treeNode*> &q)
{
	if (head == NULL)
		return;
	inOrderToQueue(head->left, q);
	q.push(head);
	inOrderToQueue(head->right, q);
}

treeNode* convert1(treeNode* head)
{
	queue<treeNode*> q;
	inOrderToQueue(head, q);
	if (q.empty())
		return head;
	head = q.front();
	q.pop();
	treeNode* pre = head;
	treeNode* cur = NULL;
	pre->left = NULL;
	while (!q.empty())
	{
		cur = q.front();
		q.pop();
		pre->right = cur;
		cur->left = pre;
		pre = cur;
	}
	pre->right = NULL;
	return head;
}

/*第二种方法参考剑指offer,实现时间复杂度为O(n),空间复杂度为
O(1)*/
void convertTreeToList(treeNode* pNode, treeNode** pLastNodeInList)
{
	if (pNode == NULL)
		return;
	treeNode* pCur = pNode;
	if (pCur->left != NULL)
		convertTreeToList(pCur->left, pLastNodeInList);
	pCur->left = *pLastNodeInList;
	if (*pLastNodeInList != NULL)
		(*pLastNodeInList)->right = pCur;
	*pLastNodeInList = pCur;
	if (pCur->right != NULL)
		convertTreeToList(pCur->right, pLastNodeInList);
}

treeNode* convert2(treeNode* pRoot)
{
	if (pRoot == NULL)
		return NULL;
	treeNode* pLastNodeInList = NULL;
	convertTreeToList(pRoot, &pLastNodeInList);
	treeNode* pHeadofList = pLastNodeInList;
	while (pHeadofList != NULL && pHeadofList->left != NULL)
		pHeadofList = pHeadofList->left;
	return pHeadofList;
}

单链表的选择排序

题目

给定一个无序单链表的头节点head,实现单链表的选择排序。要求额外空间复杂度为O(1)
主要考察调整链表的技巧,详细分析可见原书,具体代码如下

代码

#include<iostream>
using namespace std;

struct node {
	int value;
	node* next;
	node(int val)
	{
		value = val;
	}
};

node* createList(int* in, int len)
{
	node* head = new node(in[0]);
	node* p = head;
	for (int i = 1; i < len; i++)
	{
		node* pNode = new node(in[i]);
		p->next = pNode;
		p = p->next;
	}
	p->next = NULL;
	return head;
}

void printList(node* head)
{
	if (head == NULL)
		cout << "List NULL" << endl;
	node* cur = head;
	cout << "The whole list:" << endl;
	while (cur != NULL)
	{
		cout << cur->value << " ";
		cur = cur->next;
	}
	cout << endl;
}
/*单链表的选择排序,额外空间复杂度为O(1),时间复杂度为O(n2)*/
node* getSmallestNode(node* head)
{
	node* smallPre = NULL;
	node* small = head;
	node* pre = head;
	node* cur = head->next;
	while (cur != NULL)
	{
		if (cur->value < small->value)
		{
			smallPre = pre;
			small = cur;
		}
		pre = cur;
		cur = cur->next;
	}
	return smallPre;
}

node* selectionSort(node* head)
{
	node* tail = NULL; //未排序部分尾部
	node* cur = head;  //未排序部分头部
	node* smallPre = NULL; //最小节点的前一个节点
	node* small = NULL; //最小节点
	while (cur != NULL)
	{
		small = cur;
		smallPre = getSmallestNode(cur);
		if (smallPre != NULL)
		{
			small = smallPre->next;
			smallPre->next = small->next;
		}
		cur = cur == small ? cur->next : cur;
		if (tail == NULL)
			head = small;
		else
			tail->next = small;
		tail = small;
	}
	return head;
}

int main()
{
	int input[] = { 2, 4, 3, 8, 6, 1 };
	int len = 6;
	node* head1 = createList(input, len);
	printList(head1);
	node* p1 = selectionSort(head1);
	printList(p1);
	getchar();
	return 0;
}

将几个链表问题写作为一个小的合集,代码有问题指出还望大佬赐教

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值