2. 链表
2.1 移除链表元素
思路1: 原地删除法 先是对头节点的处理,如果头节点一个或几个连着都是需要删除的val,向后移动的过程中保存tmp进行delete;接着是对待后面的节点,定义一个cur指针,cur->next是要删除的元素,删除之需要cur->next = cur->next->next即可
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
while(head != nullptr && head->val == val)
{
ListNode* tmp = head;
head = head->next;
delete tmp;
}
ListNode* cur = head;
while(cur && cur->next)
{
if(cur->next->val == val)
{
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else cur = cur->next;
}
return head;
}
};
思路2:虚拟头节点 建立一个虚拟头节点,指向原本的头节点,定义一个cur同理删除对应节点,最后将dummyHead->head赋值给head, 删除虚拟头节点返回即可
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while(cur && cur->next){
if(cur->next->val == val){
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}else{
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
2.2 设计链表
定义_size和虚拟头节点_dummyHead
初始化:虚拟头节点和_size的初始化
get获取:边界判断,走到对应位置获取,记得是第index个,从dummyHead->next开始走index步即可(举个例子想想,第0个节点是不是只要走0次)
addAtHead头插入:生成新节点,先连右边后连左边,最后别忘了_size++
addAtTail最后插入:生成新节点,走到最后一个节点,进行插入,别忘了_size++
addAtIndex:越界判断(过小过大都不行),生成新节点,从虚拟头,走index步,正好在要插入的第index个元素的前一个元素,进行插入,最后_size++
deleteAtIndex: 越界判断,走到第index的前一个,删除元素(删完临时节点记得置空),别忘了--_size
printLinkedList: 按序打印即可
class MyLinkedList {
public:
// struct LinkedNode{
// int val;
// LinkedNode* next;
// LinkedNode(int val):val(val),next(nullptr){}
// };
struct LinkedNode {
// int val;
// LinkedNode* next;
// LinkedNode(int val):val(val), next(nullptr){}
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
MyLinkedList() {
//初始化
_dummyHead = new LinkedNode(0);
_size = 0;
}
int get(int index) {
// if(index<0 || index > _size - 1)return -1;
// LinkedNode* cur = _dummyHead->next;
// while(index--)
// {
// cur = cur->next;
// }
// return cur->val;
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){ // 如果--index 就会陷入死循环
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
// LinkedNode* node = new LinkedNode(val);
// node->next = _dummyHead->next;
// _dummyHead->next = node;
// _size++;
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
void addAtTail(int val) {
// LinkedNode* cur = _dummyHead;
// while(cur->next)
// {
// cur = cur->next;
// }
// LinkedNode* node = new LinkedNode(val);
// cur->next = node;
// //node->next = nullptr;
// _size++;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
void addAtIndex(int index, int val) {
if(index<0 || index> _size)return;//不能加等号,第_size的地方是可以被插入的
LinkedNode* cur = _dummyHead;
while(index--)
{
cur = cur->next;
}
LinkedNode* node = new LinkedNode(val);
node->next = cur->next;
cur->next = node;
_size++;
}
void deleteAtIndex(int index) {
// if(index<0)return;
// if(index >= _size) return;
// LinkedNode* cur = _dummyHead;
// while(index--)cur = cur->next;
// LinkedNode* tmp = cur->next;
// cur->next = cur->next->next;
// delete tmp;
// tmp->next = nullptr;
// _size--;
if (index >= _size || index < 0) {
return;
}
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur ->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp=nullptr;
_size--;
}
}
private:
int _size;
LinkedNode* _dummyHead;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
2.3 反转链表
思路1:双指针,实际上是三指针,还有一个是临时保存的指针,初始状态一个指向空pre,一个指向第一个元素cur,临时指针tmp指向第二个元素,向后迭代,直到cur为空反转完成
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//定义双指针
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur)
{
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
思路2:递归的方法,递归子函数是reverse(pre,cur), 子函数的终止条件是cur走到空,整体思路是双指针的思路,终结过程是获取tmp,走一下,再重新cur,temp迭代,有点晦涩难懂
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverse(ListNode* pre, ListNode* cur){
if(cur == nullptr)return pre;
ListNode* tmp = cur->next;
cur->next = pre;
//cur = pre;
return reverse(cur,tmp);
}
ListNode* reverseList(ListNode* head) {
return reverse(nullptr,head);
}
};
2.4 两两交换链表中的节点
需要补充一个虚拟头节点dummyHead, 交换两个节点需要三次连接步骤,需要保存第一和第三个节点,只要第一第二个节点不为空,就一直往后迭代,最后可以释放虚拟头(也可以不释放,写一下更好吧)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
//建立虚拟头节点
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while(cur->next && cur->next->next)
{
//保存第一第三个节点
ListNode* tmp = cur->next;
ListNode* tmp1 = cur->next->next->next;
//三次连接
cur->next = cur->next->next;
cur->next->next = tmp;
tmp->next = tmp1;
cur = tmp;
}
head = dummyHead->next;
delete dummyHead;
dummyHead = nullptr;
return head;
}
};
2.5 删除链表的倒数第N个节点
使用一个虚拟头节点,快慢指针都从dummyhead出发,删除倒数第n个,就先让快指针走n步,再一起走,结束条件是快指针的下一个是空的时候,删除慢指针的后一个元素即可,C++记得释放内存
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
//先让fast走n步
while(fast && n--)
{
fast = fast->next;
}
//一起走直到fast->next为空
while(fast->next)
{
fast = fast->next;
slow = slow->next;
}
//删除操作,删的是slow的后一个,c++记得要释放,所以要保存节点
ListNode* tmp = slow->next;
slow->next = slow->next->next;
delete tmp;
tmp = nullptr;
return dummyHead->next;//dummyHead算法题一般不用删
}
};
2.6 链表相交
先统计两条链表分别的长度len1和len2,计算长的减短的,让长的先走len2-len1步,再一起走,如果指针相同则找到,两个都走到空就没找到
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int len1 = 0, len2 = 0;
ListNode* p1 = headA;
ListNode* p2 = headB;
while(p1)
{
len1++;
p1 = p1->next;
}
while(p2)
{
len2++;
p2 = p2->next;
}
p1 = headA;
p2 = headB;
//让len1是长的那个
if(len1 < len2)
{
swap(p1,p2);
swap(len1,len2);
}
int sub = len1 - len2;
//1先走sub步
while(sub--)
{
p1 = p1->next;
}
while(p1 && p2)
{
if(p1 == p2) return p1;
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
};
2.7 环形链表 II
使用快慢指针来做,fast一次走两步,slow一次走一步,根据数学推理,如果有环,两个指针会在圈内的某一种处相遇,设一个index1指向相遇的位置,再定义一个index2指向head,一起走,由于数学公式,两者刚好相遇
(x + y) * 2 = x + y + n (y + z)
x = (n - 1) (y + z) + z
当 n为1的时候,公式就化解为 x = z,
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head,*slow = head;
//因为fast一次走两个位置,所以fast和fast->next都不为空才可以
while(fast && fast->next)
{
fast = fast->next->next;//快指针一次走两个位置
slow = slow->next;
if(slow == fast)//相遇
{
ListNode* index1 = slow;//index1保存相遇点
ListNode* index2 = head;//index2保存head
while(index1 != index2)
{
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return NULL;
}
};