[toc]
01移除链表元素
Problem: 203. 移除链表元素
思路
看到这题的第一想法是java处理链表会比较麻烦,因为没有指针。但是后面发现可以用对象完全替代指针
解题方法
加入一个虚节点,实现化繁为简的操作,可以统一处理所有的节点。然后通过cur!=null为终止条件进行遍历,当cur的值=val时删除该节点,java里删除不需要考虑到内存释放的问题,只需要pre.next=cur.next即可。
复杂度
时间复杂度: 每个节点遍历一次,算法复杂度取决于节点数量
空间复杂度: O(1)用了一个辅助空间
Code
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy = new ListNode();
dummy.next = head;
ListNode pre = dummy;
ListNode cur = head;//前置节点
while (cur != null) {
if (cur.val == val) {
pre.next = cur.next;
} else {
pre = cur;
}
cur = cur.next;
}
return dummy.next;
}
}
02设计链表
Problem: 707. 设计链表
[TOC]
思路
讲述看到这一题的思路
解题方法
描述你的解题方法
复杂度
时间复杂度:
添加时间复杂度, 示例:
空间复杂度:
添加空间复杂度, 示例:
Code
class MyLinkedList {
public:
class LinkedListNode{
public:
int val;
LinkedListNode* next;
LinkedListNode(int val):val(val),next(nullptr){}
};
MyLinkedList() {
_dummyhead = new LinkedListNode(0);//直接将虚节点指向新建的节点,同时size置0使得链表为空
_size=0;
}
int get(int index) {
// if(index>(_size-1) || index<0){
// return -1;
// }
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedListNode* cur = _dummyhead;
while(index--){
cur = cur->next;//指向下一个节点,直到i=index,这时候cur正好停在index的前一个
}
return cur->next->val;
}
void addAtHead(int val) {
LinkedListNode* newNode = new LinkedListNode(val);
newNode->next=_dummyhead->next;
_dummyhead->next = newNode;
_size++;
}
void addAtTail(int val) {
LinkedListNode* newNode = new LinkedListNode(val);
LinkedListNode* cur = _dummyhead;
while(cur->next != nullptr){
cur=cur->next;
}//cur移动到最后一个节点
cur->next=newNode;
_size++;
}
void addAtIndex(int index, int val) {
if(index<0 || index > (_size)){
return;//无返回值函数也可以使用返回语句,作用是结束函数
}
LinkedListNode* newNode = new LinkedListNode(val);
LinkedListNode* cur = _dummyhead;
while(index--){
cur = cur->next;//指向下一个节点,直到index=0,这时候cur正好停在index的前一个
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
return;
}
void deleteAtIndex(int index) {
if(index<0 || index > (_size-1)){
return;//无返回值函数也可以使用返回语句,作用是结束函数
}
LinkedListNode* cur = _dummyhead;
while(index--){
cur = cur->next;//指向下一个节点,直到index=0,这时候cur正好停在index的前一个
}
LinkedListNode* tmp = cur->next;//使用tmp保存需要释放的指针,因为待会儿cur的next就要被覆盖了
cur->next = tmp->next;
delete tmp ;
_size--;
return;
}
private:
LinkedListNode* _dummyhead;
int _size;
};
/**
* 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);
*/
总结:index也表示从0节点到index节点的距离,所以当index--等于0的时候,那么恰好移动了index步;index插入的时候,假如链表有3个元素,那么index是可以等于3的,等价于尾插法。
03交换链表中的相邻节点
Problem: 24. 两两交换链表中的节点
[TOC]
思路
想到的是用双指针法,最后优化发现可以去掉一个指针。另外读题刚开始也没读懂,以为只交换一对就行
解题方法
用pre获取前驱节点,每次处理两个节点,p1,p2代表后续的第一个和第二个节点,链表顺序要从后往前考虑,先把p1插进去,再插p2进去
##复杂度
时间复杂度:
会遍历所有的节点,每两个节点就会有3次指针变换op,复杂度为
空间复杂度:
空间复杂度, 示例:
Code
/**
* 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* dummy = new ListNode(0, head); // 引入虚节点简化操作
ListNode *p1, *p2, *pre;
pre = dummy;
while (pre->next != nullptr && pre->next->next != nullptr) {
// 记录两个需要交换的指针
p1 = pre->next;
p2 = p1->next;
// 交换
p1->next = p2->next;
p2->next = p1;
pre->next = p2;
// pre往后移动
pre = p1;
}
return dummy->next;
}
};
04删除倒数第n个节点
Problem: 19. 删除链表的倒数第 N 个结点
[TOC]
思路
双指针法,一个先跑一个后面跑
复杂度
时间复杂度:
n
空间复杂度:
1有一个虚节点
Code
/**
* 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 *newHead = new ListNode(0,head);
ListNode *end, *pre_n;
end = newHead;
while (n--) {
end = end->next;
} // 往前n步走
pre_n = newHead;
while (end->next != nullptr) {
pre_n = pre_n->next;
end = end->next;
}
ListNode *tmp =pre_n->next;
pre_n->next = pre_n->next->next;
delete tmp;
return newHead->next;
}
};
05链表相交
Problem: 面试题 02.07. 链表相交
[TOC]
解题方法
遇到查找问题,需要从要查找的东西的本质出发,而交点的本质就是有相同的指针地址,那么如何找到这个地址呢?就需要考虑到链表的长度,当两个链表一样长的时候,就可以同时到达相交节点。
复杂度
时间复杂度:
两个链表各遍历一次复杂度为,求相交复杂度小于n,
空间复杂度:
Code
/**
* 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 La,Lb;
ListNode *pa,*pb;
pa = headA;
La = 0;
pb = headB;
Lb = 0;
while(pa){
pa = pa->next;
La++;
}
while(pb){
pb = pb->next;
Lb++;
}
int diff;
pa =headA;
pb = headB;
//如果a长a就先走,b长b就先走,直到两个指针指到同一个位置去
if(La>Lb){
diff = La-Lb;
while(diff--){
pa = pa->next;
}
}
else{
diff = Lb-La;
while(diff--){
pb = pb->next;
}
}
while(pa&&pa!=pb){
pa = pa->next;
pb = pb ->next;
}
return pa;
}
};
06判断是否有环
Problem: 142. 环形链表 II
[TOC]
解题方法
假设环的长度为n,快指针速度为f,慢指针速度为s,那么有f-
复杂度
时间复杂度:
添加时间复杂度, 示例:
空间复杂度:
添加空间复杂度, 示例:
Code
/**
* 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;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
// 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};