1、链表
21.合并两个有序链表
题解;
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
解法一:递归法
ListNode *mergeTwoLists(ListNode *l1, ListNode *l2)
{
if (l1 == nullptr)
{
return l2;
}
if (l2 == nullptr)
{
return l1;
}
if (l1->val < l2->val)
{
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else
{
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
解法二:迭代法
#include
using namespace std;
struct ListNode
{
int val;
struct ListNode *next;
ListNode(int x) //:val(x), next(NULL){}
{
this->val = x;
this->next = NULL;
}
};
ListNode *mergeTwoLists(ListNode *l1, ListNode *l2)
{
ListNode *head = new ListNode(-1); //哑节点简化代码
ListNode q = head;
while (l1 != nullptr && l2 != nullptr)
{
if (l1->val <= l2->val)
{
q->next = l1;
l1 = l1->next;
}
else
{
q->next = l2;
l2 = l2->next;
}
q = q->next;
}
if (l1)
q->next = l1;
else
{
q->next = l2;
}
Listnode newHead=head->next;
delete head;
return newHead;
}
void test()
{
ListNode *p1 = new ListNode(1);
ListNode *p2 = new ListNode(2);
ListNode *p3 = new ListNode(4);
p1->next = p2;
p2->next = p3;
ListNode *p4 = new ListNode(1);
ListNode *p5 = new ListNode(3);
ListNode *p6 = new ListNode(4);
p4->next = p5;
p5->next = p6;
ListNode *phead = mergeTwoLists(p1, p4);
while (phead)
{
cout << phead->val << " ";
phead = phead->next;
}
}
int main()
{
test();
system(“pause”);
return 0;
}
160.相交链表
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在节点 c1 开始相交。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
方法一: 暴力法
对链表A中的每一个结点 ai,遍历整个链表 B 并检查链表 B 中是否存在结点和 ai 相同。
时间复杂度O(m * n) 空间复杂度O(1)
class Solution {
public:
//返回相交的节点
ListNode *getIntersectionNode(ListNode headA, ListNode headB) {
ListNode cur_a = headA;//为什么需要缓存结点
while (cur_a)
{
ListNode* cur_b = headB;
while (cur_b)
{
if (cur_a == cur_b)
{
return cur_a;
}
cur_b = cur_b->next;
}
cur_a = cur_a->next;
}
return nullptr;
}
};
方法二:双指针法
A的指针遍历完A 接着从headB开始遍历
B的指针遍历完B 接着从headA开始遍历
两个指针都最多会运行m + n次,当都为空时,表示不相交;当相等时,表示相交
让pA和pB分别指向链表A和B的首地址。同时开始访问,一旦一个指针到达末尾,比如pA到达末尾,则将指针指向链表B的首地址,继续遍历,同理pB到达末尾,则将指针指向链表A的首地址,继续遍历。当pA==pB则返回。
这里相当于大家都一共要走A+B链表长度这么长。而如果有公共节点的话,后面那段肯定是公共的。当两个指针同时到达末尾,则没有公共节点。
例如:A=[1,3,5,7,9] B=[2,4,7,9]
则pB相当于要走[2,4,7,9,1,3,5,7,9] pA相当于要走[1,3,5,7,9,2,4,7,9] 注意有公共节点,7,9,也就是说后面一定会遇到!
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA == nullptr || headB == nullptr)
return nullptr;
ListNode* cur_a = headA;
ListNode* cur_b = headB;
while(cur_a != cur_b)
{
cur_a = (cur_a == nullptr ? headB : cur_a->next);
cur_b = (cur_b == nullptr ? headA : cur_b->next);
两种写法
cur1=cur1?cur1->next:headB;
cur2=cur2?cur2->next:headA;
}
return cur_a;
}
};
206.反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
1、用栈来解决问题
class Solution {
public:
ListNode* reverseList(ListNode* head) {
stack<ListNode*> stk;
ListNode* pt = head;//可以用auto代替ListNode*
while(pt)
{
stk.push(pt);
pt = pt->next;
}
ListNode* dummy = new ListNode(0);
pt = dummy;//不用pt也行 但需要再定义一个指针来缓存这个dummy 存疑
while(stk.size())//这里pt又指向了新的链表 让栈里元素弹出 赋值给pt
{
pt->next = stk.top();
stk.pop();
pt = pt->next;
}
pt->next = nullptr;//这步必须有
ListNode* newHead = dummy->next;
delete dummy;
return newHead;
}
};
2、头插法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* newHead = new ListNode(-1);
while (head != nullptr) {
ListNode* q = head->next;
head->next = newHead->next;//每次都让旧结点插入新头结点的下一个 这和栈的实现非常的相似 注意不能写q= newHead->next
newHead->next = head;
head = q;//这里不能直接写head=head->next 需要先将head->next缓存一下 因为需要让head指向其他地方
}
return newHead->next;
}
};
这么写错哪了//q->next 的值换了
ListNode* newHead = new ListNode(-1);
ListNode* q=head;
while(q)
{
q->next=newHead->next;//这里
newHead->next=q;
q=q->next;
}
return newHead->next;
- 删除排序链表中的重复元素?
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
1、双指针法
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head){//不判断是否为空 提交会报错
ListNode* cur = head->next, pre = head;
while(cur){
if(cur->val == pre->val){
pre->next = cur->next;
delete cur;
cur = pre->next;
}else{
pre = cur;
cur = cur->next;
}
}
}
return head;
}
};
不明白返回的是head
或者前面判断是否为空这么写if(headNULL||head->nextNULL)
return head;
2、递归法
class Solution {
public:
ListNode deleteDuplicates(ListNode* head) {
if(!head||!head->next)
return head;
head->next=deleteDuplicates(head->next);
if(head->val==head->next->val) head=head->next;
return head;
}
};
递归套路解决链表问题:
找终止条件:当head指向链表只剩一个元素的时候,自然是不可能重复的,因此return
想想应该返回什么值:应该返回的自然是已经去重的链表的头节点
每一步要做什么:宏观上考虑,此时head.next已经指向一个去重的链表了,而根据第二步,我应该返回一个去重的链表的头节点。因此这一步应该做的是判断当前的head和head.next是否相等,如果相等则说明重了,返回head.next,否则返回head
237.删除链表中的节点
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 – head = [4,5,1,9],它可以表示为:
示例 1:
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
替身攻击
class Solution {
public:
void deleteNode(ListNode* node) {
node->val=node->next->val;
node->next=node->next->next;
}
};
19.删除链表的倒数第 n 个节点?
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
双指针
解题思路与官方所给方法相同,使用两个指针分别指向头节点和第n个节点,当后一个指针到达最后一个节点时,第一个指针正好位于倒数第n+1个节点处,删除第一个指针的下一个结点即可。
我的理解:倒数第n个结点,正数为总结点数-n+1,让一个指针指向第n个结点,另一个结点从头开始遍历,一起出发,第一个走完时,第二个指针指向总数-n即倒数第n+1个结点
代码
/**
- Definition for singly-linked list.
- struct ListNode {
-
int val;
-
ListNode *next;
-
ListNode(int x) : val(x), next(NULL) {}
- };
/
class Solution {
public:
ListNode removeNthFromEnd(ListNode* head, int n) {
ListNode* first = new ListNode(NULL);
first->next = head; //添加头节点,便于操作
ListNode* ptr = first; //搜索节点
ListNode* current = first; //中间节点
for (int i = 0; i < n; i++)//将该指针指第n个节点
{
current = current->next;
}
while (current->next != NULL) //到达最后一个节点
{
ptr = ptr->next;
current = current->next;
}
ListNode* p = ptr->next;
ptr->next = p->next; //删除ptr指向节点
delete p; //释放空间
return first->next;
}
两个问题:1、为什么delete的不是first new谁delete谁?
2、为什么提交return head会报错
24.两两交换链表中的结点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
双指针
class Solution {
public:
ListNodeswapPairs(ListNodehead){
ListNode*dummHead=newListNode(0);
dummHead->next=head;
ListNodep=dummHead;
while(p->next&&p->next->next){
ListNodenode1=p->next;
ListNodenode2=node1->next;
ListNodenode3=node2->next;
p->next=node2;
node2->next=node1;
node1->next= node3;//顺序可颠倒
p=node1;//p=p->next但是p->next改变了所以用node1
}
ListNode*retNode=dummHead->next;
deletedummHead;//为了deletenew出来的结点所以多写两行
returnretNode;
}
};
445、两数相加
给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
进阶:
如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
示例:
输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出: 7 -> 8 -> 0 -> 7
两栈头插法
或者可以用三栈解决
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
if(!l1)
return l2;
if(!l2)
return l1;
stacks1,s2;
//把l1和l2放进栈里
while(l1)
{
s1.push(l1->val);
l1=l1->next;
}
while(l2)
{
s2.push(l2->val);
l2=l2->next;
}
int carry = 0,n1 = 0,n2 = 0,sum = 0;
ListNode* dummy=new ListNode(-1);
while(!s1.empty() || !s2.empty() || carry)
{
if(s1.empty())n1 = 0;//这里必须要判断 因为while中或是全为假才跳出循环 即s1s2都为空 carry也为0
else {n1 = s1.top();s1.pop();}
if(s2.empty())n2 = 0;
else{n2 = s2.top();s2.pop();}
sum=n1+n2+carry;//求和与进位值
ListNode* tmp=new ListNode(sum%10);//插入取余的值
tmp->next=dummy->next;
dummy->next=tmp;
carry=sum/10;//处理进位 取整不要余数
}
ListNode* newHead=dummy->next;
delete dummy;
return newHead;
}
};
234、回文链表
Vector数组 双指针
/**
-
Definition for singly-linked list.
-
struct ListNode {
-
int val;
-
ListNode *next;
-
ListNode(int x) : val(x), next(NULL) {}
-
};
/
class Solution {
public:
bool isPalindrome(ListNode head) {
if(headNULL) return true;
if(head->nextNULL)return true;ListNode *i=head; vector<int> val; //将链表值放入数组 while(i!=NULL){ val.push_back(i->val); i=i->next; } int m=0,n=val.size()-1; while(m<n){ if(val[m]!=val[n]) return false; m++; n--; } return true;
}
};
栈 一一出栈比较
class Solution {
public:
bool isPalindrome(ListNode* head) {
stack s;
ListNode *p = head;
while§{
s.push(p->val);
p = p->next;
}
p = head;
while§{
if(p->val != s.top()){
return 0;
}
s.pop();
p = p->next;
}
return 1;
}
};
- 移除链表元素
/**
- Definition for singly-linked list.
- struct ListNode {
-
int val;
-
ListNode *next;
-
ListNode(int x) : val(x), next(NULL) {}
- };
/
class Solution {
public:
ListNode removeElements(ListNode* head, int val) {
ListNode* newHead=new ListNode(-1);
newHead->next=head;
ListNode* p=newHead;
while(p->next)
{
if(p->next->val==val)
{
p->next=p->next->next;
}
else
p=p->next;
}
return newHead->next;
}
};