顺序表、单链表

目录

一,线性表

(1)顺序表的创建、插入、删除、查找、合并

(2)用顺序表实现一元n次多项式的加法运算

(3)链表的创建、插入、删除、查找、合并

(4)用链表实现一元n次多项式的加法运算

(5)设计实现一个处理100位以内的长整数加减运算的程序

二,OJ实战

力扣 19. 删除链表的倒数第N个节点 (链表的离散跳跃)

力扣 82. 删除排序链表中的重复元素 II

力扣 83. 删除排序链表中的重复元素

力扣 92. 反转链表 II

力扣 25. K 个一组翻转链表

力扣 143. 重排链表

力扣 234. 回文链表

力扣 369. 给单链表加一

力扣 1474. 删除链表 M 个节点之后的 N 个节点

力扣 1634. 求两个多项式链表的和

力扣 1669. 合并两个链表

力扣 2487. 从链表中移除节点

力扣 3063. 链表频率

剑指 Offer 06. 从尾到头打印链表

剑指 Offer 25. 合并两个排序的链表

剑指 Offer 35. 复杂链表的复制

剑指 Offer II 078. 合并排序链表

力扣 面试题 02.01. 移除重复节点

力扣 面试题 02.04. 分割链表

力扣 面试题 02.05. 链表求和

力扣 面试题 02.06. 回文链表

力扣 面试题 02.07. 链表相交


一,线性表

这一章其实是大学实验报告。

线性表按照存储元素的结构,可以分为顺序表和链表。

(1)顺序表的创建、插入、删除、查找、合并

程序:

#include<iostream>
using namespace std;

int main()

{
	//顺序表的实现
	struct linear_list_polynomial//定义结构体数组
	{
		int degree;//多项式的次数
		double coefficient;//多项式的系数
	};

	int max_degree;
	cout << "enter the max_degree" << endl;
	cin >> max_degree;//max_degree是最大次数,分配max_degree这么多空间
	cout << "enter degree and coefficient , enter -1,-1 for end" << endl;
	struct linear_list_polynomial *pointer = new struct linear_list_polynomial[max_degree];
	int length_of_linear_list = 0;//记录链表的长度
	for (int i = 0; i < max_degree; i++)//输入,初始化

	{
		cin >> pointer[i].degree >> pointer[i].coefficient;
		if (pointer[i].degree == -1)break;
		length_of_linear_list++;
	}
	cout << "the linear list is" << endl;
	for (int i = 0; i < length_of_linear_list; i++)//输出
		cout << pointer[i].degree << " " << pointer[i].coefficient << "     ";

	//插入操作
	int i;//在第i个元素之前插入1个元素
	cout << endl << endl << "在第i个元素之前插入1个元素,输入i" << endl;
	cin >> i;
	if (i<1 || i>length_of_linear_list)cout << "enter error!";
	else
	{
		for (int j = length_of_linear_list; j>i - 2; j--)
			pointer[j + 1] = pointer[j];
		cout << "enter what you what to insert" << endl;
		cin >> pointer[i - 1].degree >> pointer[i - 1].coefficient;
		length_of_linear_list++;
		for (int i = 0; i < length_of_linear_list; i++)//输出
			cout << pointer[i].degree << " " << pointer[i].coefficient << "     ";
	}

	//删除操作
	int i2;//删除第i2个元素
	cout << endl << endl << "删除第i个元素,输入i" << endl;
	cin >> i2;
	if (i2<1 || i2>length_of_linear_list)
		cout << "enter error!";
	else
	{
		for (int j = i2; j<length_of_linear_list; j++)
			pointer[j - 1] = pointer[j];
		length_of_linear_list--;
		for (int i = 0; i < length_of_linear_list; i++)//输出
			cout << pointer[i].degree << " " << pointer[i].coefficient << "     ";
	}

	//查找操作
	int number_find;
	cout << endl << endl << "enter the number you find" << "   ";
	cin >> number_find;
	for (int i = 0; i < length_of_linear_list; i++)//输出
	{
		if (pointer[i].degree == number_find)
		{
			cout << number_find << "是第" << i + 1 << "个元素";
			break;
		}
	}
	return 0;
}

运行结果:

(2)用顺序表实现一元n次多项式的加法运算

程序:

#include<iostream>
using namespace std;

int main()
{
	struct linear_list_polynomial			//定义结构体数组
	{
		int degree;
		double coefficient;
	};

	int length_of_linear_list1;				//第一个多项式的项数
	cout << "enter length_of_linear_list     ";
	cin >> length_of_linear_list1;
	cout << "enter degree and coefficient" << endl;
	struct linear_list_polynomial *pointer1 = new struct linear_list_polynomial[length_of_linear_list1];
	for (int i = 0; i < length_of_linear_list1; i++)			//输入,初始化
		cin >> pointer1[i].degree >> pointer1[i].coefficient;
	cout << "the polynomial  is" << endl;
	for (int i = 0; i < length_of_linear_list1; i++)			//输出
	{
		if (i != 0)cout << "  +  ";
		cout << pointer1[i].coefficient << "*x^" << pointer1[i].degree;
	}

	int length_of_linear_list2;				//第二个多项式的项数
	cout << endl << endl << "enter length_of_linear_list   ";
	cin >> length_of_linear_list2;
	cout << "enter degree and coefficient" << endl;
	struct linear_list_polynomial *pointer2 = new struct linear_list_polynomial[length_of_linear_list2];
	for (int i = 0; i < length_of_linear_list2; i++)			//输入,初始化
		cin >> pointer2[i].degree >> pointer2[i].coefficient;
	cout << "the polynomial  is" << endl;
	for (int i = 0; i < length_of_linear_list2; i++)			//输出
	{
		if (i != 0)cout << "  +  ";
		cout << pointer2[i].coefficient << "*x^" << pointer2[i].degree;
	}

	int length_of_linear_list3 = length_of_linear_list1 + length_of_linear_list2;			//两个多项式的和
	struct linear_list_polynomial *pointer3 = new struct linear_list_polynomial[length_of_linear_list3];
	int i1 = 0, i2 = 0, i3 = 0;
	while (i1 < length_of_linear_list1&&i2 < length_of_linear_list2)
	{
		if (pointer1[i1].degree < pointer2[i2].degree)
		{
			pointer3[i3].degree = pointer1[i1].degree;
			pointer3[i3].coefficient = pointer1[i1].coefficient;
			i1++;
		}
		else if (pointer1[i1].degree > pointer2[i2].degree)
		{
			pointer3[i3].degree = pointer2[i2].degree;
			pointer3[i3].coefficient = pointer2[i2].coefficient;
			i2++;
		}
		else
		{
			pointer3[i3].degree = pointer1[i1].degree;
			pointer3[i3].coefficient = pointer1[i1].coefficient + pointer2[i2].coefficient;
			i1++,i2++;
		}
		i3++;
	}
	if (i1 < length_of_linear_list1)
		while (i1 < length_of_linear_list1)
		{
			pointer3[i3].degree = pointer1[i1].degree;
			pointer3[i3].coefficient = pointer1[i1].coefficient;
			i1++,i3++;
		}
	else
		while (i2< length_of_linear_list2)
		{
			pointer3[i3].degree = pointer2[i2].degree;
			pointer3[i3].coefficient = pointer2[i2].coefficient;
			i2++,i3++;
		}
	cout << endl << endl << "the sum of the two polynomials are" << endl;
	for (int i = 0; i < i3; i++)			//输出
	{
		if (i != 0)cout << "  +  ";
		cout << pointer3[i].coefficient << "*x^" << pointer3[i].degree;
	}
	return 0;
}

运行结果:

(3)链表的创建、插入、删除、查找、合并

程序:

#include<iostream>
using namespace std;

int main()
{
	//链表的实现

	struct Polynomial		//定义结构体数组
	{
		int degree;			//多项式的次数
		double coefficient;			//多项式的系数
		struct Polynomial *next;		//后继指针
	};
	Polynomial head;			//头结点
	Polynomial *p;				//用于各种操作的临时指针
	int length_of_linear_list = 0;			//记录链表的长度
	head.next = NULL;
	int temp_degree;		//用来判断是否继续输入
	double temp_coefficient;
	cout << "enter coefficient and degree ,enter -1,-1 for end  " << endl;
	while(true)
	{
		p = new struct Polynomial();			//创建新结点
		cin >> temp_coefficient >> temp_degree;		//输入
		if (temp_degree < 0)break;
		p->coefficient = temp_coefficient;
		p->degree = temp_degree;
		p->next = head.next;			//在头结点和第一个结点之间插入刚刚申请的这个结点
		head.next = p;
		length_of_linear_list++;
	}
	p = head.next;
	cout << "the polynomial is   ";
	while (p != NULL)
	{
		if (p != head.next)cout << " + ";
		cout << p->coefficient << "*x^" << p->degree;		//输出
		p = p->next;			//指针后移
	}

	//插入操作
	int i;				//在第i个元素之前插入1个元素
	cout << endl << endl << "在第i个元素之前插入1个元素,输入i    ";
	cin >> i;
	if (i<1 || i>length_of_linear_list)cout << "enter error!";
	else
	{
		int position = 0;			//记录指针p目前的位置
		Polynomial *insert;
		insert = new struct Polynomial();			//申请新结点
		cout << endl << "enter what you what to insert    ";
		cin >> insert->coefficient >> insert->degree;		//输入
		p = &head;
		while (p != NULL && position< i - 1)		//将p定位到i-1的位置
		{
			p = p->next;
			position++;
		}
		insert->next = p->next;		//在p和p的后继之间插入刚刚申请的insert
		p->next = insert;
		p = head.next;
		cout << "the polynomial is   ";
		while (p != NULL)
		{
			if (p != head.next)cout << " + ";
			cout << p->coefficient << "*x^" << p->degree;		//输出
			p = p->next;			//指针后移
		}
	}

	//删除操作
	int i2;				//删除第i2个元素
	cout << endl << endl << "删除第i个元素,输入i   ";
	cin >> i2;
	if (i2<1 || i2>length_of_linear_list)cout << "enter error!";
	else
	{
		int position = 0;			//记录指针p目前的位置
		p = &head;
		while (p != NULL && position< i2 - 1)		//将p定位到i-1的位置
		{
			p = p->next;
			position++;
		}
		Polynomial *temp;		//用temp保存要删掉的结点
		temp = p->next;
		p->next = temp->next;
		free(temp);
	}
	p = head.next;
	cout << "the polynomial is   ";
	while (p != NULL)
	{
		if (p != head.next)cout << " + ";
		cout << p->coefficient << "*x^" << p->degree;		//输出
		p = p->next;			//指针后移
	}

	//查找操作
	int number_find;
	cout << endl << endl << "enter the number you find" << "   ";
	cin >> number_find;
	p = head.next;
	int position = 1;			//记录指针p目前的位置
	while (p->degree != number_find)
	{
		p = p->next;
		position++;
	}
	cout << number_find << "是第" << position << "个元素";
	return 0;
}


运行结果:

(4)用链表实现一元n次多项式的加法运算

代码:

#include<iostream>
using namespace std;

int main()
{
	struct Polynomial		//定义结构体数组
	{
		int degree;			//多项式的次数
		double coefficient;			//多项式的系数
		struct Polynomial *next;		//后继指针
	}head1,head2,head3,*p,*q,*r;

	//第一个多项式
	head1.next = NULL;
	cout << "enter coefficient and degree ,enter -1,-1 for end  " << endl;
	while(true)
	{
		p = new struct Polynomial();			//创建新结点
		cin >> p->coefficient >> p->degree;		//输入
		if (p->degree < 0)break;
		p->next = head1.next;			//在头结点和第一个结点之间插入刚刚申请的这个结点
		head1.next = p;
	}
	cout << "the polynomial is   ";
	p = head1.next;
	while (p != NULL)
	{
		if (p != head1.next)cout << " + ";
		cout << p->coefficient << "*x^" << p->degree;		//输出
		p = p->next;			//指针后移
	}

	//第二个多项式
	head2.next = NULL;
	cout << endl << "enter coefficient and degree ,enter -1,-1 for end  " << endl;
	while(true)
	{
		q = new struct Polynomial();			//创建新结点
		cin >> q->coefficient >> q->degree;		//输入
		if (q->degree < 0)break;
		q->next = head2.next;			//在头结点和第一个结点之间插入刚刚申请的这个结点
		head2.next = q;
	}
	cout << "the polynomial is   ";
	q = head2.next;
	while (q != NULL)
	{
		if (q != head2.next)cout << " + ";
		cout << q->coefficient << "*x^" << q->degree;		//输出
		q = q->next;			//指针后移
	}

	//第三个多项式,表示和
	head3.next = NULL;
	p = head1.next, q = head2.next;
	while (p != NULL && q != NULL)
	{
		r = new struct Polynomial();			//创建新结点
		if (p->degree == q->degree)
		{
			r->coefficient = p->coefficient + q->coefficient;
			r->degree = p->degree;
			p = p->next;
			q = q->next;
			if (r->coefficient==0)continue;
		}
		else if (p->degree > q->degree)
		{
			r = q;
			q = q->next;
		}
		else
		{
			r = p;
			p = p->next;			
		}
		r->next = head3.next;
		head3.next = r;
	}
	r = head3.next;
	cout << "\nthe sum is   ";
	while (r != NULL)
	{
		if (r != head3.next)cout << " + ";
		cout << r->coefficient << "*x^" << r->degree;		//输出
		r = r->next;			//指针后移
	}
	return 0;
}

结果:

(5)设计实现一个处理100位以内的长整数加减运算的程序

程序:

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

class very_big_number
{
public:
	int section_of_number;
	very_big_number(int number)						//每9位为1段
	{
		section_of_number = number;
	}
	int *p_section_list = new int[section_of_number];
	void input()
	{
		cout << "输入数字,每次输入9位   ";
		for (int i = section_of_number; i >0; i--)cin >> p_section_list[i];
	}
	void output()
	{
		cout << "the number is    " << p_section_list[section_of_number];
		for (int i = section_of_number - 1; i >0; i--)cout << setw(9) << setfill('0') << p_section_list[i];
		cout << endl;
	}
};

int main()
{
	int section_of_number1;
	cout << "enter section_of_number1      ";
	cin >> section_of_number1;
	very_big_number number1(section_of_number1);
	number1.input();
	number1.output();
	int section_of_number2;
	cout << "enter section_of_number2     ";
	cin >> section_of_number2;
	very_big_number number2(section_of_number2);
	number2.input();
	number2.output();


	int sum_section;
	if (section_of_number1 < section_of_number2)sum_section = section_of_number2 + 1;
	else sum_section = section_of_number1 + 1;			//计算2个数的和
	very_big_number sum_of_them(sum_section);
	for (int i = 1; i < sum_section + 1; i++)
	{
		sum_of_them.p_section_list[i] = 0;
	}
	int max = 999999999;
	int if_into = 0;
	int j = 1;
	for (; j < section_of_number1 + 1 && j < section_of_number2 + 1; j++)
	{
		int temp = number1.p_section_list[j] + number2.p_section_list[j] + if_into;
		if (temp > max)
		{
			if_into = 1;
			sum_of_them.p_section_list[j] = temp - max - 1;
		}
		else
		{
			if_into = 0;
			sum_of_them.p_section_list[j] = temp;
		}
	}
	int *p = number1.p_section_list;
	if (j < section_of_number1 + 1)    p = number1.p_section_list;
	else if (j < section_of_number2 + 1)	p = number2.p_section_list;
	for (; j< sum_section; j++)
	{
		int temp = p[j] + if_into;
		if (temp > max)
		{
			if_into = 1;
			sum_of_them.p_section_list[j] = temp - max - 1;
		}
		else
		{
			if_into = 0;
			sum_of_them.p_section_list[j] = temp;
		}
	}
	if (if_into)sum_of_them.p_section_list[j] = 1;
	cout << endl << "the sum of the is    ";
	sum_of_them.output();
	cout << "end";
	system("pause>nul");
	return 0;
}

运行结果:

二,OJ实战

力扣 19. 删除链表的倒数第N个节点 (链表的离散跳跃)

题目:

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

分析:

第一个想到的思路就是双指针,但是当n较小时,这个思路其实就是等同于扫描了2遍。

双指针一起扫描一遍和一个指针扫描2遍真的有区别吗?我认为没区别。

而我给出了基本上可以说是真正只扫描了一遍的算法:

链表的离散跳跃

当n比较小时,比如n<10,还是双指针,但是可以每次往前跳10步,一个指针要遍历10个节点,另外一个指针可以直接跳过去。

当n比较大时,比如n>100,每次跳10步的话,就需要保存很多中间指针,不可取。

我的算法是,如果n>100,那么每次跳n/10步,只需要保存11个指针即可。

注意,这里需要用到循环覆盖的技巧。

代码:

#define m 10
#define m2 100
typedef pair<ListNode*, int>thePair;
 
class Solution {
public:
	thePair move(ListNode* p, int k)
	{
		while (k && p)
		{
			k--,p = p->next;
		}
		return make_pair(p, k);
	}
	ListNode* removeNthFromEnd(ListNode* head, int n) {
		ListNode* p = head;
		ListNode* point[m + 1];//存跳跃点
		int nplus = n + m - n % m;
		int dif = max(nplus / m, m2);//单次跳跃距离
		int num = 0;//跳跃次数
		thePair pa;
		while (true)
		{
			pa = move(p, dif);
			if (pa.second > 0)break;
			point[num % (m + 1)] = pa.first;
			num++;
		}
		int dist = dif - pa.second;
		while (dist < n)
		{
			num = (num + m) % (m + 1);
			dist += dif;
		}
		ListNode* ans = point[num];
		if (num == 0)ans = head;
		if (dist == n)return head->next;
		while (dist > n+1)
		{
			ans = ans->next;
			dist--;
		}
		ans->next = ans->next->next;
		return head;
	}
};

力扣 82. 删除排序链表中的重复元素 II

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:

输入: 1->2->3->3->4->4->5
输出: 1->2->5
示例 2:

输入: 1->1->1->2->3
输出: 2->3

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        map<int,int>m;
        ListNode*p=head;
        while(p)m[p->val]++,p=p->next;
        p=head;
        while(p && m[p->val]>1)p=p->next;
        head=p;
        while(p)
        {
            while(p->next && m[p->next->val]>1)p->next=p->next->next;
            p=p->next;
        }
        return head;
    }
};

力扣 83. 删除排序链表中的重复元素

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

示例 1:


输入:head = [1,1,2]
输出:[1,2]
示例 2:


输入:head = [1,1,2,3,3]
输出:[1,2,3]
 

提示:

链表中节点数目在范围 [0, 300] 内
-100 <= Node.val <= 100
题目数据保证链表已经按升序 排列

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode*p=head;
        while(head && head->next)if(head->val==head->next->val)head->next=head->next->next;
        else head=head->next;
        return p;
    }
};

力扣 92. 反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
 

示例 1:


输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:

输入:head = [5], left = 1, right = 1
输出:[5]
 

提示:

链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
 

进阶: 你可以使用一趟扫描完成反转吗?

class Solution {
public:
	ListNode* reverseBetween(ListNode* head, int left, int right) {
		ListNode* p = head;
		for (int i = 0; i < left - 2; i++)p = p->next;
		ListNode* pleft = p;
		if (left > 1)pleft = p->next;
		ListNode* pright = pleft;
		for (int i = 0; i < right - left; i++)pright = pright->next;
		ListNode* p2 = pright->next;
		pright->next = NULL;
		LinkReverse(pleft);
		pleft->next = p2;
		if (left > 1)p->next = pright;
		else return pright;
		return head;
	}
};

力扣 25. K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2:

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

提示:

  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

class Solution {
public:
	ListNode* reverseKGroup(ListNode* head, int k) {
		ListNode* p = LinkSplit(head, k);
		if (!p) {
			if(LinkGetLength(head)<k)return head;
			else return LinkReverse(head);
		}
		ListNode* h2 = LinkReverse(head);
		head->next = reverseKGroup(p, k);
		return h2;
	}
};

力扣 143. 重排链表

题目:

给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:

给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

思路:

分为三步:

(1)算出链表长度,从中间断开,变成2个链表

(2)后半段链表反转,输出新的head即原来的tail

(3)把2个链表交叉串起来。

代码:

 
ListNode* mergeTwoLists(ListNode* p, ListNode* q) {
        if(!p)return q;
        if(!q)return p;
        ListNode *pnext,*qnext,*ans=p;
        while(p && q)
        {
            if(p->next==NULL && q->next!=NULL)
            {
                p->next=q;
                break;
            }
            pnext=p->next, qnext=q->next;
            p->next=q, q->next=pnext;
            p=pnext, q=qnext;
        }
        return ans;
    }
 
class Solution {
public:
    void reorderList(ListNode* head) {
        if(head==NULL)return;
        int length=(LinkGetLength(head)-1)/2;
        ListNode* p=head;
        while(length--)p=p->next;
        ListNode*q=p->next;
        p->next=NULL; //链表割开
        q=LinkReverse(q);//后半段反转
        p=head;
        mergeTwoLists(p,q);
    }
};

做题时候调试用的测试代码:

int main()
{
    ListNode *p1=new ListNode(1);
    ListNode *p2=new ListNode(2);
    ListNode *p3=new ListNode(3);
    ListNode *p4=new ListNode(4);
    p1->next=p2,p2->next=p3,p3->next=p4,p4->next=NULL;
    Solution s;
    s.reorderList(p1);
    cout<<p1->val<<p2->val<<p3->val<<p4->val<<endl;
    while(p1)
    {
        cout<<p1->val;
        p1=p1->next;
    }
    return 0;
}

输出:

1234
1423

PS:可以看出修改链表的节点存储的数值,和修改链表的链接,是不一样的。这也是题目要求的做法。

力扣 234. 回文链表

题目:

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

示例 1:

输入: 1->2
输出: false
示例 2:

输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

 
class Solution {
public:
	bool isPalindrome(ListNode* head) {
		if (head == NULL)return true;
		int length = LinkGetLength(head);
		length = (length - 1) / 2;
		ListNode* p = head;
		while (length--)p = p->next;
		ListNode*q = p->next;
		p->next = NULL; //链表割开
		q = LinkReverse(q);//后半段反转
		p = head;
		while (q)
		{
			if (p->val != q->val)return false;
			p = p->next, q = q->next;
		}
		return true;
	}
};

力扣 369. 给单链表加一

给定一个用链表表示的非负整数, 然后将这个整数 再加上 1 。

这些数字的存储是这样的:最高位有效的数字位于链表的首位 head 。

示例 1:

输入: head = [1,2,3]
输出: [1,2,4]
示例 2:

输入: head = [0]
输出: [1]
 

提示:

链表中的节点数在 [1, 100] 的范围内。
0 <= Node.val <= 9
由链表表示的数字不包含前导零,除了零本身。

class Solution {
public:
	int plusCarry(ListNode* head) {
		if (!head) return 1;
		int a = plusCarry(head->next);
		if (a == 0)return 0;
		head->val++;
		if (head->val == 10) {
			head->val = 0;
			return 1;
		}
		return 0;
	}
	ListNode* plusOne(ListNode* head) {
		int a = plusCarry(head);
		if (a == 1) {
			ListNode* p = new ListNode(1);
			p->val = 1, p->next = head, head = p;
		}
		return head;
	}
};

力扣 1474. 删除链表 M 个节点之后的 N 个节点

给定链表 head 和两个整数 m 和 n. 遍历该链表并按照如下方式删除节点:

开始时以头节点作为当前节点.
保留以当前节点开始的前 m 个节点.
删除接下来的 n 个节点.
重复步骤 2 和 3, 直到到达链表结尾.
在删除了指定结点之后, 返回修改过后的链表的头节点.

示例 1:

输入: head = [1,2,3,4,5,6,7,8,9,10,11,12,13], m = 2, n = 3
输出: [1,2,6,7,11,12]
解析: 保留前(m = 2)个结点,  也就是以黑色节点表示的从链表头结点开始的结点(1 ->2).
删除接下来的(n = 3)个结点(3 -> 4 -> 5), 在图中以红色结点表示.
继续相同的操作, 直到链表的末尾.
返回删除结点之后的链表的头结点.
示例 2:

输入: head = [1,2,3,4,5,6,7,8,9,10,11], m = 1, n = 3
输出: [1,5,9]
解析: 返回删除结点之后的链表的头结点.
示例 3:

输入: head = [1,2,3,4,5,6,7,8,9,10,11], m = 3, n = 1
输出: [1,2,3,5,6,7,9,10,11]
示例 4:

输入: head = [9,3,7,7,9,10,8,2], m = 1, n = 2
输出: [9,7,8]
 

提示:

链表中节点数目在范围 [1, 104] 内
1 <= Node.val <= 106
1 <= m, n <= 1000
 

进阶: 你能通过 就地 修改链表的方式解决这个问题吗?

class Solution {
public:
	ListNode* deleteNodes(ListNode* head, int m, int n) {
		int k = 0;
		ListNode *p1, *ans = head;
		while (head) {
			if (k % (m + n) == m - 1)p1 = head;
			if (k % (m + n) > m - 1) p1->next = head->next;
			head = head->next;
			k++;
		}
		return ans;
	}
};

力扣 1634. 求两个多项式链表的和

多项式链表是一种特殊形式的链表,每个节点表示多项式的一项。

每个节点有三个属性:

coefficient:该项的系数。项 9x4 的系数是 9 。
power:该项的指数。项 9x4 的指数是 4 。
next:指向下一个节点的指针(引用),如果当前节点为链表的最后一个节点则为 null 。
例如,多项式 5x3 + 4x - 7 可以表示成如下图所示的多项式链表:

多项式链表必须是标准形式的,即多项式必须 严格 按指数 power 的递减顺序排列(即降幂排列)。另外,系数 coefficient 为 0 的项需要省略。

给定两个多项式链表的头节点 poly1 和 poly2,返回它们的和的头节点。

PolyNode 格式:

输入/输出格式表示为 n 个节点的列表,其中每个节点表示为 [coefficient, power] 。例如,多项式 5x3 + 4x - 7 表示为: [[5,3],[4,1],[-7,0]] 。

示例 1:

输入:poly1 = [[1,1]], poly2 = [[1,0]]
输出:[[1,1],[1,0]]
解释:poly1 = x. poly2 = 1. 和为 x + 1.
示例 2:

输入:poly1 = [[2,2],[4,1],[3,0]], poly2 = [[3,2],[-4,1],[-1,0]]
输出:[[5,2],[2,0]]
解释:poly1 = 2x2 + 4x + 3. poly2 = 3x2 - 4x - 1. 和为 5x2 + 2. 注意,我们省略 "0x" 项。
示例 3:

输入:poly1 = [[1,2]], poly2 = [[-1,2]]
输出:[]
解释:和为 0。我们返回空链表。
 

提示:

0 <= n <= 104
-109 <= PolyNode.coefficient <= 109
PolyNode.coefficient != 0
0 <= PolyNode.power <= 109
PolyNode.power > PolyNode.next.power

class Solution {
public:
	PolyNode* addPoly(PolyNode* p1, PolyNode* p2) {
		if (!p1)return p2;
		if (!p2)return p1;
		if (p1->power > p2->power) {
			p1->next = addPoly(p1->next, p2);
			return p1;
		}
		if (p1->power < p2->power) {
			p2->next = addPoly(p2->next, p1);
			return p2;
		}
		p1->coefficient += p2->coefficient;
		p1->next = addPoly(p1->next, p2->next);
		if (p1->coefficient == 0)return p1->next;
		return p1;
	}
};

力扣 1669. 合并两个链表

给你两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。

请你将 list1 中下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点的位置。

下图中蓝色边和节点展示了操作后的结果:


请你返回结果链表的头指针。

示例 1:

输入:list1 = [0,1,2,3,4,5], a = 3, b = 4, list2 = [1000000,1000001,1000002]
输出:[0,1,2,1000000,1000001,1000002,5]
解释:我们删除 list1 中下标为 3 和 4 的两个节点,并将 list2 接在该位置。上图中蓝色的边和节点为答案链表。
示例 2:


输入:list1 = [0,1,2,3,4,5,6], a = 2, b = 5, list2 = [1000000,1000001,1000002,1000003,1000004]
输出:[0,1,1000000,1000001,1000002,1000003,1000004,6]
解释:上图中蓝色的边和节点为答案链表。
 

提示:

3 <= list1.length <= 104
1 <= a <= b < list1.length - 1
1 <= list2.length <= 104

class Solution {
public:
	ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
		auto p = LinkSplit(list1, a);
		LinkMerge(list1, list2);
		LinkMerge(list2, LinkSplit(p, b - a + 1));
		return list1;
	}
};

力扣 2487. 从链表中移除节点

给你一个链表的头节点 head 。

移除每个右侧有一个更大数值的节点。

返回修改后链表的头节点 head 

示例 1:

输入:head = [5,2,13,3,8]
输出:[13,8]
解释:需要移除的节点是 5 ,2 和 3 。
- 节点 13 在节点 5 右侧。
- 节点 13 在节点 2 右侧。
- 节点 8 在节点 3 右侧。

示例 2:

输入:head = [1,1,1,1]
输出:[1,1,1,1]
解释:每个节点的值都是 1 ,所以没有需要移除的节点。

提示:

  • 给定列表中的节点数目在范围 [1, 105] 内
  • 1 <= Node.val <= 105
class Solution {
public:
    ListNode* removeNodes(ListNode* head) {
        vector<ListNode*> v = listToVec(head);
        vector<ListNode*> v2;
        int n = 0;
        for(int i=v.size()-1;i>=0;i--){
            if(v[i]->val >= n){
                n = v[i]->val;
                v2.push_back(v[i]);
            }
        }
        return vecToList(frev(v2));
    }
};

力扣 3063. 链表频率

给定包含 k 个 不同 元素的链表的 head 节点,创建一个长度为 k 的链表,以 任何顺序 返回链表中所有 不同元素 出现的 频率。返回这个链表的头节点。

示例 1:

输入:head = [1,1,2,1,2,3]

输出:[3,2,1]

解释:列表中有 3 个不同的元素。1 的频率是 3,2 的频率是 2,3 的频率是 1。因此,我们返回 3 -> 2 -> 1。

注意 1 -> 2 -> 3,1 -> 3 -> 2,2 -> 1 -> 3,2 -> 3 -> 1,和 3 -> 1 -> 2 都是合法的答案。

示例 2:

输入:head = [1,1,2,2,2]

输出:[2,3]

解释:列表中有 2 个不同的元素。1 和 2 出现的频率是 2 和 3。因此,我们返回 2 -> 3。

示例 3:

输入:head = [6,5,4,3,2,1]

输出:[1,1,1,1,1,1]

解释:列表中有 6 个不同的元素。每个元素的频率是 1。因此,我们返回 1 -> 1 -> 1 -> 1 -> 1 -> 1。

提示:

  • 链表中的节点数字范围在 [1, 105]之间。
  • 1 <= Node.val <= 105
class Solution {
public:
	ListNode* frequenciesOfElements(ListNode* head) {
		auto v = ListToVec(head);
		vector<int> v2;
		transform(v.begin(), v.end(), std::inserter(v2, v2.end()), [&](const ListNode* p) {return p->val; });
		auto v3 = Fshr(v2, true);
		vector<ListNode*> v4;
		transform(v3.begin(), v3.end(), std::inserter(v4, v4.end()), [&](const pair<int, int>& apair) {return new ListNode(apair.second); });
		return VecToList(v4);
	}
};

剑指 Offer 06. 从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

输入:head = [1,3,2]
输出:[2,3,1]
 

限制:

0 <= 链表长度 <= 10000

//翻转vector
template<typename T>
vector<T> frev(vector<T> v)
{
    vector<T> ans;
    for(int i=v.size()-1;i>=0;i--)ans.push_back(v[i]);
    return ans;
}
class Solution {
public:
    vector<int> reversePrint(ListNode * head) {
        vector<int>ans;
        while(head)
        {
            ans.push_back(head->val);
            head=head->next;
        }
        return frev(ans);
    }
};

剑指 Offer 25. 合并两个排序的链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* p, ListNode* q) {    
        if(!p)return q;
        if(!q)return p;
        ListNode *head;
        if(p->val < q->val)head=p,p=p->next;
        else head=q,q=q->next;
        ListNode *ans=head;
 
        while(p && q)
        {
            if(p->val < q->val)ans->next=p,ans=p,p=p->next;
            else ans->next=q,ans=q,q=q->next;
        }
        if(p)ans->next=p;
        else ans->next=q;
        return head;
    }
};

剑指 Offer 35. 复杂链表的复制

题目:

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的 深拷贝。 

我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。
 

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
 

提示:

-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。

代码:

class Solution {
public:
	Node* copyRandomList(Node* head) {
		if (!head)return NULL;
		Node* p = head;
		map<Node*, Node*>m;
		while (p)
		{
			Node* tmp = new Node(p->val);
			m[p] = tmp, p = p->next;
		}
		p = head;
		while (p)
		{
			m[p]->next = m[p->next], m[p]->random = m[p->random], p = p->next;
		}
		return m[head];
	}
};

剑指 Offer II 078. 合并排序链表

给定一个链表数组,每个链表都已经按升序排列。

请将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:

输入:lists = []
输出:[]
示例 3:

输入:lists = [[]]
输出:[]
 

提示:

k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的总和不超过 10^4

//把两个升序的链表合并为一个升序的链表
ListNode* mergeTwoUpLists(ListNode* p, ListNode* q) {
	if (!p)return q;
	if (!q)return p;
	ListNode* head;
	if (p->val < q->val)head = p, p = p->next;
	else head = q, q = q->next;
	ListNode* ans = head;
	while (p && q)
	{
		if (p->val < q->val)ans->next = p, ans = p, p = p->next;
		else ans->next = q, ans = q, q = q->next;
	}
	if (p)ans->next = p;
	else ans->next = q;
	return head;
}
class Solution {
public:
	ListNode* mergeKLists(vector<ListNode*>& lists) {
		ListNode* ans = NULL;
		for (auto& li : lists)ans = mergeTwoUpLists(ans, li);
		return ans;
	}
};

力扣 面试题 02.01. 移除重复节点

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

示例1:

 输入:[1, 2, 3, 3, 2, 1]
 输出:[1, 2, 3]

示例2:

 输入:[1, 1, 1, 1, 2]
 输出:[1, 2]

提示:

  1. 链表长度在[0, 20000]范围内。
  2. 链表元素在[0, 20000]范围内。

进阶:

如果不得使用临时缓冲区,该怎么解决?

class Solution {
public:
    ListNode* removeDuplicateNodes(ListNode* head) {
        m.clear();
        if(head)m[head->val]=1,removeDuplicateNodes(head,head->next);
        return head;
    }
    void removeDuplicateNodes(ListNode* fa,ListNode* p){
        if(!p)return;
        if(m[p->val])fa->next=p->next,removeDuplicateNodes(fa,fa->next);
        else m[p->val]=1,removeDuplicateNodes(p,p->next);
    }
    map<int,int>m;
};

力扣 面试题 02.04. 分割链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你不需要 保留 每个分区中各节点的初始相对位置。

示例 1:

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

示例 2:

输入:head = [2,1], x = 2
输出:[1,2]

提示:

  • 链表中节点的数目在范围 [0, 200] 内
  • -100 <= Node.val <= 100
  • -200 <= x <= 200
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        vector<ListNode*> v=ListToVec(head);
        vector<ListNode*>v2;
        for(auto vi:v)if(vi->val<x)v2.push_back(vi);
        for(auto vi:v)if(vi->val>=x)v2.push_back(vi);
        return VecToList(v2);
    }
};

力扣 面试题 02.05. 链表求和

给定两个用链表表示的整数,每个节点包含一个数位。

这些数位是反向存放的,也就是个位排在链表首部。

编写函数对这两个整数求和,并用链表形式返回结果。

示例:

输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912

进阶:思考一下,假设这些数位是正向存放的,又该如何解决呢?

示例:

输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295
输出:9 -> 1 -> 2,即912
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        vector<ListNode*>v;
        int s=0;
        while(l1 || l2){
            int x=s;
            if(l1)x+=l1->val;
            if(l2)x+=l2->val;
            s=x/10;
            v.push_back(new ListNode(x%10));
            if(l1)l1=l1->next;
            if(l2)l2=l2->next;
        }
        if(s)v.push_back(new ListNode(s));
        return VecToList(v);
    }
};

力扣 面试题 02.06. 回文链表

编写一个函数,检查输入的链表是否是回文的。

示例 1:

输入: 1->2
输出: false 

示例 2:

输入: 1->2->2->1
输出: true 

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        int len=LinkGetLength(head);
        if(len<2)return true;
        auto p=LinkSplit(head,len/2);
        if(len&1)p=p->next;
        p=LinkReverse(p);
        return IsSameLinkList(head,p);
    }
};

力扣 面试题 02.07. 链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交

​编辑

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

​编辑

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

​编辑

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

​编辑

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 0 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listA 和 listB 没有交点,intersectVal 为 0
  • 如果 listA 和 listB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]

进阶:你能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int len1=LinkGetLength(headA),len2=LinkGetLength(headB);
        while(len1>len2)headA=headA->next,len1--;
        while(len1<len2)headB=headB->next,len2--;
        while(headA!=headB)headA=headA->next,headB=headB->next;
        return headA;
    }
};

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值