链表系列

 

目录

剑指Offer(6)--从尾到头打印链表 

剑指Offer(22)--链表中倒数第k个节点

剑指Offer(24)--反转链表

剑指Offer(25)--合并两个排序的链表

剑指Offer(35)--复杂链表的复制

剑指Offer(23)--链表中环的入口节点

剑指Offer(52)--两个链表的第一个公共节点

剑指Offer(18)--删除有序链表中重复的节点

剑指Offer(62)--圆圈中最后剩下的数字

两个单链表生成相加链表

         K个一组翻转链表

回文链表

链表(拆分、翻转、合并)


剑指Offer(6)--从尾到头打印链表 

#include<stack>
#include<iostream>
using namespace std;
struct ListNode
{
	int m_value;
	ListNode *m_next;
};
//用stack实现的,而递归本质上就是一个栈结构,所以也可以用递归来实现
void printListReversing_1(ListNode *node)
{
	stack<ListNode*> sList;
	while (node != nullptr)
	{
		sList.push(node);
		node = node->m_next;
	}
	while (!sList.empty())
	{
		cout << sList.top()->m_value << "	";
		sList.pop();
	}
}
//用递归的方式实现
void printListReversing_2(ListNode *node)
{
	if (node != nullptr)
	{
		if (node->m_next != nullptr)
			printListReversing_2(node->m_next);
		cout << node->m_value << "	";
	}
}

剑指Offer(22)--链表中倒数第k个节点

#include<iostream>
using namespace std;
struct ListNode
{
	int m_value;
	ListNode *m_next;
};
/*
输入一个链表,输出该链表中倒数第k个节点
*/
//分成两个指针走,使它俩的距离相差k-1
int printListNode(ListNode *node, int k)
{
	if (node == nullptr || k <= 0)
		return -1;
	ListNode *firstNode = node;
	ListNode *secondNode = node;
	for (int i = 0; i < k-1; ++i)
	{
		if (firstNode->m_next == nullptr)
		{
			cout << "链表个数小于k" << endl;
			return -1;
		}
		firstNode = firstNode->m_next;
	}
	while (firstNode->m_next != nullptr)
	{
		firstNode = firstNode->m_next;
		secondNode = secondNode->m_next;
	}
	return secondNode->m_value;
}
int main(void)
{
	ListNode *m_node1 = new ListNode();
	ListNode *m_node2 = new ListNode();
	ListNode *m_node3 = new ListNode();
	ListNode *m_node4 = new ListNode();
	m_node1->m_value = 1;
	m_node1->m_next = m_node2;
	m_node2->m_value = 2;
	m_node2->m_next = m_node3;
	m_node3->m_value = 3;
	m_node3->m_next = m_node4;
	m_node4->m_value = 4;
	m_node4->m_next = nullptr;
	int result = printListNode(m_node1,5);
	cout << result;
	system("pause");
	return 0;
}

剑指Offer(24)--反转链表

递归

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode *newHead = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return newHead;
    }
};

非递归

#include<iostream>
using namespace std;
struct ListNode
{
	int m_value;
	ListNode *m_next;
};
/*
用三个指针解决问题
*/
ListNode *reverseList(ListNode *node)
{
	if (node == nullptr || node->m_next == nullptr)
		return nullptr;
	ListNode *preNode = node;
	ListNode *currentNode = node->m_next;
	ListNode *nextNode = nullptr;
	preNode->m_next = nullptr;
	while (currentNode != nullptr)
	{
		nextNode = currentNode->m_next;
		currentNode->m_next = preNode;
		preNode = currentNode;
		currentNode = nextNode;
	}
	return preNode;
}
void printListValue(ListNode *node)
{
	while (node != nullptr)
	{
		cout << node->m_value << " ";
		node = node->m_next;
	}
	cout << endl;
}
int main(void)
{
	ListNode *m_node1 = new ListNode();
	ListNode *m_node2 = new ListNode();
	ListNode *m_node3 = new ListNode();
	ListNode *m_node4 = new ListNode();
	ListNode *m_node5 = new ListNode();
	m_node1->m_value = 1;
	m_node1->m_next = m_node2;
	m_node2->m_value = 2;
	m_node2->m_next = m_node3;
	m_node3->m_value = 3;
	m_node3->m_next = m_node4;
	m_node4->m_value = 4;
	m_node4->m_next = nullptr;
	m_node5 = reverseList(m_node1);
	printListValue(m_node5);
	system("pause");
	return 0;
}

剑指Offer(25)--合并两个排序的链表

比如是两个递增排序的链表

#include<iostream>
using namespace std;
struct ListNode
{
	int m_value;
	ListNode *m_next;
};
//递归版本
ListNode *mergeList(ListNode *node1, ListNode *node2)
{
	if (node1 == nullptr)
		return node2;
	else if (node2 == nullptr)
		return node1;
	ListNode *mergeHead = nullptr;
	if (node1->m_value < node2->m_value)
	{
		mergeHead = node1;
		mergeHead->m_next = mergeList(node1->m_next, node2);
	}
	else
	{
		mergeHead = node2;
		mergeHead->m_next = mergeList(node1, node2->m_next);
	}
	return mergeHead;
}

剑指Offer(35)--复杂链表的复制

在复杂链表中,每个节点除了有一个m_next指针指向下一个节点,还有一个m_rand指针指向链表中的任意节点或者nullptr。

#include<iostream>
#include<map>
using namespace std;
struct ListNode
{
	int m_value;
	ListNode *m_next;
	ListNode *m_rand;
};
/*
方法一:两个链表是通过map串起来的,原始的链表
节点作为键,copy的链表作为值,然后根据键的->next
和->rand,找值的->next和->rand
*/
ListNode *copyListWithRand1(ListNode *head)
{
	map<ListNode*, ListNode*> m;
	ListNode *curNode = head;
	//先构造出待复制的全部结点
	while (curNode != nullptr)
	{
		ListNode *copyNode = new ListNode();
		copyNode->m_value = curNode->m_value;
		m.insert(pair<ListNode*, ListNode*>(curNode, copyNode));
		curNode = curNode->m_next;
	}
	curNode = head;
	//将map中的复制过来的结点串联起来
	while (curNode!= nullptr)
	{
		m.find(curNode)->second->m_next = m.find(curNode)->first->m_next;
		m.find(curNode)->second->m_rand = m.find(curNode)->first->m_rand;
		curNode = curNode->m_next;
	}
	return m.find(head)->second;
}

/*
方法二:不使用哈希表,利用的是在原来的
链表上插入copy的节点,找到rand之后再分开
*/
ListNode *copyListWithRand2(ListNode *head)
{
	if (head == nullptr)
		return nullptr;
	ListNode *curNode = nullptr;
	ListNode *nextNode = nullptr;
	/*
	比如原来是2->1->8,现在就是构成
	2->2~->1->1~->8->8~,这就实现了不使用额外数据结构,
	而只是使用有限个变量
	*/
	while (curNode != nullptr)
	{
		nextNode = curNode->m_next;
		ListNode *copyNode = new ListNode();
		copyNode->m_value = curNode->m_value;
		curNode->m_next = copyNode;
		copyNode->m_next = nextNode;
		curNode = nextNode;
	}
	curNode = head;
	ListNode *copyNode = nullptr;
	/*
	下面做的是找rand,如果2->rand=8,那么2~->rand=8~;
	*/
	while (curNode != nullptr)
	{
		//cur! = nullptr,那么cur->next必然不为nullptr,因为经过了上面的复制操作
		nextNode = curNode->m_next->m_next;
		copyNode = curNode->m_next;
		copyNode->m_rand = curNode->m_rand != nullptr ? curNode->m_rand->m_next : nullptr;
		curNode = nextNode;
	}
	curNode = head;
	ListNode *resultHead = head->m_next;
	/*
	经过上面的步骤,就找到了2~、1~、8~->rand,但是现在的copy链表
	和原链表是在一起2->2~->1->1~->8->8~,下面进行分割
	*/
	while (curNode != nullptr)
	{
		//cur!=nullptr,那么cur->next必然不为nullptr,因为经过了上面的复制操作
		nextNode = curNode->m_next->m_next;
		copyNode = curNode->m_next;
		curNode->m_next = nextNode;
		copyNode->m_next = nextNode != nullptr ? nextNode->m_next : nullptr;
		curNode = nextNode;
	}
	return resultHead;
}

剑指Offer(23)--链表中环的入口节点

#include<iostream>
using namespace std;
struct ListNode
{
	int m_value;
	ListNode *m_next;
};
/*
判断单链表是否有环,如果有环,返回第一个入环的节点;
如果无环,返回NULL
准备两个指针,一个是快指针,一个是慢指针;快指针一次
走两步,慢指针一次走一步。如果快指针在走的过程中遇到
NULL,直接返回无环;如果有环,快指针和慢指针一定会在
环上相遇。相遇的时候快指针回到开始处,然后快指针由一次
走两步变为一次走一步,然后快慢指针一起走,相遇的节点
即为入环的节点。
*/
ListNode *getLoopListNode(ListNode *head)
{
	if (head == NULL || head->m_next == NULL || head->m_next->m_next == NULL)
		return NULL;
	ListNode *n1 = head->m_next;//慢指针
	ListNode *n2 = head->m_next->m_next;//快指针
	while (n1 != n2)
	{
		if (n2->m_next == NULL || n2->m_next->m_next == NULL)
			return NULL;
		n1 = n1->m_next;
		n2 = n2->m_next->m_next;
	}
	n2 = head;//将n2重新指向头节点
	while (n1 != n2)
	{
		n1 = n1->m_next;
		n2 = n2->m_next;
	}
	return n1;
}

数学证明

如果存在环,快慢指针一定会相遇:

1、如果链表没有环,那么快指针比慢指针先到达尾部(null)。

2、如果链表有环的话,因为快指针走的比慢指针快,所以在环中相遇的过程可以看作是快指针从环后边追赶慢指针的过程。

用递归法证明,快慢指针一定会相遇:

  1. 快指针与慢指针之间差一步。此时继续往后走,慢指针前进一步,快指针前进两步,两者相遇。
  2. 快指针与慢指针之间差两步。此时继续往后走,慢指针前进一步,快指针前进两步,两者之间相差一步,转化为第一种情况。
  3. 快指针与慢指针之间差N步。此时继续往后走,慢指针前进一步,快指针前进两步,两者之间相差(N+1-2)即N-1步。重复这个过程,直到快指针和慢指针相遇。

因此,此题得证。所以快指针必然与慢指针相遇。

假设慢指针进入环中时,即连接点p,快指针需要m步才能追上慢指针。

p和q第一次相遇时,碰撞点在L处。此时,慢指针走到L时用了m步。

 假设head到p的距离为a,环长度为Length环,慢指针走了s步,则快指针走了2s步。

从上图可知:

s = a + m

2s = a + m + n * Length环(n为快指针绕环的圈数)

可得

a = n * Length环 - m

也就是:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点p。

可根据这个结论来找到入口节点。

剑指Offer(52)--两个链表的第一个公共节点

需要考虑链表是否有环,可以问面试者

#include<iostream>
using namespace std;
/*
两个单链表相交的一系列问题
【题目】 在本题中,单链表可能有环,也可能无环。给定两个
单链表的头节点 head1和head2,这两个链表可能相交,也可能
不相交。请实现一个函数, 如果两个链表相交,请返回相交的
第一个节点;如果不相交,返回null 即可。 要求:如果链表1
的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外
空间复杂度请达到O(1)。
*/
struct ListNode
{
	int m_value;
	ListNode *m_next;
};
/*
判断单链表是否有环,如果有环,返回第一个入环的节点;
如果无环,返回NULL
准备两个指针,一个是快指针,一个是慢指针;快指针一次
走两步,慢指针一次走一步。如果快指针在走的过程中遇到
NULL,直接返回无环;如果有环,快指针和慢指针一定会在
环上相遇。相遇的时候快指针回到开始处,然后快指针由一次
走两步变为一次走一步,然后快慢指针一起走,相遇的节点
即为入环的节点。
*/
ListNode *getLoopListNode(ListNode *head)
{
	if (head == NULL || head->m_next == NULL || head->m_next->m_next == NULL)
		return NULL;
	ListNode *n1 = head->m_next;//慢指针
	ListNode *n2 = head->m_next->m_next;//快指针
	while (n1 != n2)
	{
		if (n2->m_next == NULL || n2->m_next->m_next == NULL)
			return NULL;
		n1 = n1->m_next;
		n2 = n2->m_next->m_next;
	}
	n2 = head;//将n2重新指向头节点
	while (n1 != n2)
	{
		n1 = n1->m_next;
		n2 = n2->m_next;
	}
	return n1;
}
/*
如果loop1(入环节点)为NULL并且loop2也为NULL,
说明两个链表均无环。
下面的函数是在两个链表均无环找到第一个相交的节点
首先统计链表1和链表2的长度,并且拿到它们的末节点
得到长度len1和指针end1,长度len2和指针end2,先判断
end1是否等于end2(这里的相等是指内存地址,说明是
同一个节点,而不是指值),如果相等说明相加,反之,
不相交。如果end1等于end2,说明相加,但这个节点未必
是第一个相交的节点,要找第一个相交的节点,应该是len长
的那个链表先走距离差值的节点数,然后一起走,遇到相等的
节点即为第一个相交节点
*/
ListNode *noLoop(ListNode *head1, ListNode *head2)
{
	if (head1 == NULL || head2 == NULL)
		return NULL;
	ListNode *cur1 = head1;
	ListNode *cur2 = head2;
	int n1 = 1, n2 = 1;
	while (cur1->m_next != NULL)
	{
		++n1;
		cur1 = cur1->m_next;
	}
	while (cur2->m_next != NULL)
	{
		++n2;
		cur2 = cur2->m_next;
	}
	if (cur1 != cur2)
		return NULL;
	int n = n1 - n2;
	cur1 = head1;
	cur2 = head2;
	while (n > 0)
	{
		cur1 = cur1->m_next;
		--n;
	}
	while (n < 0)
	{
		cur2 = cur2->m_next;
		++n;
	}
	//cout << n << endl;
	while (cur1 != cur2)
	{
		cur1 = cur1->m_next;
		cur2 = cur2->m_next;
	}
	return cur1;
}
/*
如果一个链表有环,一个链表无环,结论是不可能相交
*/
/*
如果是两个有环链表,有三种情况
1.各自环各自的,不相交
2.先相交,然后共享同一个环
3.\    /,长得和天线宝宝一样
	\ /
	 O

如果loop1==loop2就是上面的第二种结构,如果loop1!=loop2,
则有1、3两种情况,此时从loop1开始继续走,直到又绕回
loop1还没有loop2这样一个节点,那就是情况1,否则就是情况3
*/
ListNode *bothLoop(ListNode *head1, ListNode *loop1, ListNode *head2, ListNode *loop2)
{
	ListNode *cur1 = NULL;
	ListNode *cur2 = NULL;
	//先各走的,然后一起环一个
	if (loop1 == loop2)
	{
		cur1 = head1;
		cur2 = head2;
		int n1 = 0, n2 = 0;
		while (cur1 != loop1)
		{
			++n1;
			cur1 = cur1->m_next;
		}
		while (cur2 != loop2)
		{
			++n2;
			cur2 = cur2->m_next;
		}
		int n = n1 - n2;
		cur1 = head1;
		cur2 = head2;
		while (n > 0)
		{
			cur1 = cur1->m_next;
			--n;
		}
		while (n < 0)
		{
			cur2 = cur2->m_next;
			++n;
		}
		while (cur1 != cur2)
		{
			cur1 = cur1->m_next;
			cur2 = cur2->m_next;
		}
		return cur1;
	}
	else
	{
		cur1 = loop1->m_next;
		while (cur1 != loop1)
		{
			if (cur1 == loop2)
				return loop1;
			cur1 = cur1->m_next;
		}
		//走到这说明是第一种情况,不相交
		return NULL;
	}
}
//接口函数
ListNode *getIntersectListNode(ListNode *head1, ListNode *head2)
{
	if (head1 == NULL || head2 == NULL) {
		return NULL;
	}
	ListNode *loop1 = getLoopListNode(head1);
	ListNode *loop2 = getLoopListNode(head2);
	if (loop1 == NULL && loop2 == NULL) {
		return noLoop(head1, head2);
	}
	if (loop1 != NULL && loop2 != NULL) {
		return bothLoop(head1, loop1, head2, loop2);
	}
	return NULL;
}

剑指Offer(18)--删除有序链表中重复的节点

1->2->2->3->4->4->5,经过下面函数处理之后:

1->3->5

有返回值就不需要**head

ListNode* deleteDuplicates(ListNode* head)
 {
	 if (head == NULL)
		 return head;
	 ListNode *node = head;
	 ListNode *preNode = NULL;
	 while (node != NULL)
	 {
		bool needDelete = false;
		ListNode *nextNode = node->next;
		if (nextNode != NULL && nextNode->val == node->val)
			needDelete = true;
		if (!needDelete)
		{
			preNode = node;
			node = node->next;
		}
		else
		{
			ListNode *deleteNode = node;
			int value = node->val;
			while (deleteNode != NULL && deleteNode->val == value)
			{
				nextNode = deleteNode->next;
				delete deleteNode;
				deleteNode = NULL;
				deleteNode = nextNode;
			}
			if (preNode == NULL)
				head = nextNode;
			else
				preNode->next = nextNode;
			node = nextNode;
		}
	 }
	 return head;
 }
#include<iostream>
using namespace std;
struct ListNode
{
	int m_value;
	ListNode *m_next;
};
void DeleteDuplication(ListNode **pHead)
{
	if (pHead == nullptr || *pHead == nullptr)
		return;
	ListNode *pPreNode = nullptr;//pPreNode指向前一个不重复的节点
	ListNode *pNode = *pHead;//pNode指向待验证的节点
	while (pNode != nullptr)
	{
		ListNode *pNext = pNode->m_next;
		bool needDelete = false;
		if (pNext != nullptr && pNext->m_value == pNode->m_value)
			needDelete = true;
		if (!needDelete)
		{
			pPreNode = pNode;
			pNode = pNode->m_next;
		}
		//说明某节点的值至少重复一次
		else
		{
			int value = pNode->m_value;
			ListNode *pDelete = pNode;
			while (pDelete != nullptr && pDelete->m_value == value)
			{
				pNext = pDelete->m_next;
				delete pDelete;
				pDelete = nullptr;

				pDelete = pNext;
			}
			//头节点发生了改变(因为有重复节点)
			if (pPreNode == nullptr)
				*pHead = pNext;
			else
				pPreNode->m_next = pNext;
			pNode = pNext;
		}
	}
}
void printList(ListNode *head)
{
	while (head != nullptr)
	{
		cout << head->m_value << " ";
		head = head->m_next;
	}
	cout << endl;
}
int main(void)
{
	ListNode *m_ListNode1 = new ListNode();
	ListNode *m_ListNode2 = new ListNode();
	ListNode *m_ListNode3 = new ListNode();
	ListNode *m_ListNode4 = new ListNode();
	ListNode *m_ListNode5 = new ListNode();
	m_ListNode1->m_value = 1;
	m_ListNode1->m_next = m_ListNode2;
	m_ListNode2->m_value = 1;
	m_ListNode2->m_next = m_ListNode3;
	m_ListNode3->m_value = 2;
	m_ListNode3->m_next = m_ListNode4;
	m_ListNode4->m_value = 3;
	m_ListNode4->m_next = nullptr;
	DeleteDuplication(&m_ListNode1);
	printList(m_ListNode1);
	system("pause");
	return 0;
}

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。 

 ListNode* deleteDuplicates(ListNode* head)
 {
	 if (head == nullptr)
		 return head;
	 ListNode *node = head;
	 while (node != NULL)
	 {
		 ListNode *nextNode = node->next;
		 bool needDelete = false;
		 if (nextNode != NULL && node->val == nextNode->val)
			 needDelete = true;
		 if (!needDelete)
			 node = node->next;
		 else
		 {
			 int value = node->val;
			 ListNode *deleteNode = nextNode;
			 while (deleteNode != NULL && deleteNode->val == value)
			 {
				 nextNode = deleteNode->next;
				 delete deleteNode;
				 deleteNode = NULL;
				 deleteNode = nextNode;
			 }
			 node->next = nextNode;
			 node = nextNode;
		 }
	 }
	 return head;
 }

剑指Offer(62)--圆圈中最后剩下的数字

题目:0, 1, … , n-1 这 n 个数字排成一个圈圈,从数字 0 开始每次从圆圏里删除第 m 个数字。求出这个圈圈里剩下的最后一个数字。

思路:

利用list模拟一个环形链表,每当迭代器扫描到链表尾部的时候,手动将迭代器移到链表头部。以此模拟环状。

#include<iostream>
#include<list>
using namespace std;
//0,1.....n-1个数字,每次删除的是第m个数字
int LastRemaining(int n, int m)
{
	if (n < 1 || m < 1)
		return -1;
	list<int> l;
	for (int i = 0; i < n; ++n)
		l.push_back(i);
	auto current = l.begin();
	while (l.size() > 1)
	{
		//先找到删除的位置
		for (int i = 1; i < m; ++i)
		{
			++current;
			if (current == l.end())
				current = l.begin();
		}
		//current指向的位置,就是本次删除的元素
		//所以要记录current的下一个位置,下次就是从next开始
		auto next = ++current;
		//手动维持一个环状链表
		if (next == l.end())
			next = l.begin();
		--current;
		l.erase(current);
		current = next;
	}
	return *current;
}

两个单链表生成相加链表

题目

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

#include<iostream>
using namespace std;
struct ListNode {
	int value;
	ListNode *next;
	ListNode(int v) :value(v), next(nullptr) {}
};
//链表反转
ListNode *reverseList(ListNode *node)
{
	if (node == nullptr || node->next == nullptr)
		return nullptr;
	ListNode *preNode = node;
	ListNode *currentNode = node->next;
	ListNode *nextNode = nullptr;
	preNode->next = nullptr;
	while (currentNode != nullptr)
	{
		nextNode = currentNode->next;
		currentNode->next = preNode;
		preNode = currentNode;
		currentNode = nextNode;
	}
	return preNode;
}
ListNode *addList(ListNode *node1, ListNode *node2)
{
	if (node1 == nullptr)
		return node2;
	if (node2 == nullptr)
		return node1;
	ListNode *root = new ListNode(-1);
	ListNode *reverseNode1 = reverseList(node1);
	ListNode *reverseNode2 = reverseList(node2);
	int value1 = 0, value2 = 0;
	int h = 0;//进位
	int value = 0;//每次相向的结果
	ListNode *listnode = root;
	while (reverseNode1 != nullptr || reverseNode2 != nullptr)
	{
		value1 = reverseNode1 != nullptr ? reverseNode1->value : 0;
		value2 = reverseNode2 != nullptr ? reverseNode2->value : 0;
		value = value1 + value2 + h;
		ListNode *node = new ListNode(value % 10);
		listnode->next = node;
		listnode = node;
		h = value / 10;
		reverseNode1 = reverseNode1!= nullptr ? reverseNode1->next : nullptr;
		reverseNode2 = reverseNode2!= nullptr ? reverseNode2->next : nullptr;
	}
	while (h != 0)
	{
		ListNode *node = new ListNode(h % 10);
		listnode->next = node;
		h /= 10;
	}
	ListNode *result = reverseList(root->next);
	return result;
}

K个一组翻转链表

#include<iostream>
using namespace std;
/*
给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序。
示例 :

给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
*/
struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(NULL) {}
};
//递归,时间复杂度O(n),空间复杂度O(1)
ListNode* reverseKGroup(ListNode* head, int k)
{
	if (head == nullptr || head->next == nullptr || k < 2)
		return head;
	ListNode *next_group = head;
	for (int i = 0; i < k; ++i)
	{
		if (next_group)
			next_group = next_group->next;
		else
			return head;
	}
	//next_group是下一组k个节点的头
	//new_next_group是下一组k个节点的新的头
	//1->2->3<-4,然后3->nullptr,所以,此时,next_group是3,
	//而new_next_group是4
	ListNode *new_next_group = reverseKGroup(next_group, k);
	ListNode *prev = nullptr, *cur = head;
	//注意:这里判断指向的是next_group
	while (cur!= next_group)
	{
		ListNode *next = cur->next;
		cur->next = prev ? prev : new_next_group;
		prev = cur;
		cur = next;
	}
	return prev;
}
int main(void)
{
	system("pause");
	return 0;
}

回文链表

请判断一个链表是否为回文链表。

方法一:

使用快慢指针找中点的原理是fast和slow两个指针,每次快指针走两步,慢指针走一步,等快指针走完时,慢指针的位置就是中点。我们还需要用栈,每次慢指针走一步,都把值存入栈中,等到达中点时,链表的前半段都存入栈中了,由于栈的后进先出的性质,就可以和后半段链表按照回文对应的顺序比较了。

struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(NULL) {}
};
bool isPalindrome(ListNode* head) 
{
	if (!head || !head->next) return true;
	ListNode *slow = head, *fast = head;
	stack<int> s;
	s.push(head->val);
	while (fast->next && fast->next->next) {
		slow = slow->next;
		fast = fast->next->next;
		s.push(slow->val);
	}
	//链表节点个数是奇数的时候,去除最中间节点
	if (fast->next==nullptr) 
		s.pop();
	while (slow->next) {
		slow = slow->next;
		int tmp = s.top(); s.pop();
		if (tmp != slow->val) return false;
	}
	return true;
}

方法二:

我们可以在找到链表中点后,将后半段的链表翻转一下,这样我们就可以按照回文的顺序比较了。

bool isPalindrome(ListNode* head)
{
	if (head == nullptr || head->next == nullptr)
		return true;
	ListNode *n1 = head;
	ListNode *n2 = head;
	while (n2->next != nullptr && n2->next->next != nullptr)
	{
		n1 = n1->next;
		n2 = n2->next->next;
	}
	n2 = n1->next;
	n1->next = nullptr;
	ListNode *nextNode = nullptr;
	while (n2 != nullptr)
	{
		nextNode = n2->next;
		n2->next = n1;
		n1 = n2;
		n2 = nextNode;
	}
	nextNode = n1;//恢复链表的时候使用
	n2 = head;
	while (n1 != nullptr && n2 != nullptr)
	{
		if (n1->val != n2->val)
			return false;
		n1 = n1->next;
		n2 = n2->next;
	}
	return true;
}

链表(拆分、翻转、合并)

1->2->3->4->5->6转为1->6->2->5->3->4。

struct ListNode {
	int value;
	ListNode *next;
	ListNode(int v) :value(v), next(nullptr) {}
};
ListNode* reorderList(ListNode *head)
{
	if (head == nullptr || head->next == nullptr)
		return head;
	ListNode *fast = head;
	ListNode *slow = head;
	//链表切割
	while (fast->next != nullptr && fast->next->next != nullptr)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	ListNode *secondList = slow->next;
	slow->next = nullptr;
	//链表翻转
	ListNode *preNode = nullptr;
	ListNode *nextNode = nullptr;
	while (secondList != nullptr)
	{
		nextNode = secondList->next;
		secondList->next = preNode;
		preNode = secondList;
		secondList = nextNode;
	}
	//链表合并
	secondList = preNode;
	ListNode *firsList = head;
	ListNode *temp1 = nullptr;
	ListNode *temp2 = nullptr;
	while (firsList != nullptr && secondList != nullptr)
	{
		temp1 = firsList->next;
		temp2 = secondList->next;
		firsList->next = secondList;
		secondList->next = temp1;
		firsList = temp1;
		secondList = temp2;
	}
	return head;
}

反转从位置 m 到 n 的链表

 ListNode *reverseBetween(ListNode *head, int m, int n) {
        ListNode *dummy = new ListNode(-1), *pre = dummy;
        dummy->next = head;
        for (int i = 0; i < m - 1; ++i) pre = pre->next;
        ListNode *cur = pre->next;
        for (int i = m; i < n; ++i) {
            ListNode *t = cur->next;
            cur->next = t->next;
            t->next = pre->next;
            pre->next = t;
        }
        return dummy->next;
    }

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值