目录
一,线性表
这一章其实是大学实验报告。
线性表按照存储元素的结构,可以分为顺序表和链表。
(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]
提示:
- 链表长度在[0, 20000]范围内。
- 链表元素在[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;
}
};