链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。
c++链表节点:
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
如果不自己定义构造函数,在初始化的时候就不能直接给变量赋值。
(1) 203 移除链表元素
(2) 设计链表
(3) 206 翻转链表
(4) 24 两两交换链表中的节点
(5) 19 删除链表的倒数第N个节点
(6) 160 链表相交
(7) 141 环形链表
(8) 142 环形链表2
------------------------------------------------
建议在c++中手动从内存中删除移除的两个节点,python不用手动管理内存。
leetcode中c++代码可以通过,只是内存大一些。
迭代法:对链表进行操作 且 返回原链表 时,可以 设置虚拟头结点 方便代码。
------------------------------------------------
(1) 203 移除链表元素
题目描述:删除链表中等于给定值 val 的所有节点。
--------------------- python ----------------------
迭代法:
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
if not head:return head
pre=ListNode(0)
pre.next=head
cur=pre
while cur.next:
if cur.next.val==val:
cur.next=cur.next.next
else:cur=cur.next
return pre.next
递归法:
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
if not head:return head
head.next=self.removeElements(head.next,val)
return head if head.val!=val else head.next
--------------------- c++ ----------------------
迭代法:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
ListNode* cur = dummyHead;
while (cur->next != NULL) {
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;
}
};
递归法:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if (head==nullptr) return head;
head->next=removeElements(head->next,val);
return head->val!=val? head: head->next;
}
};
(2)707 设计链表
题目描述:在链表类中实现这些功能:
--------------------- python ----------------------
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self.dummy_head = ListNode()
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
current = self.dummy_head.next
for i in range(index):
current = current.next
return current.val
def addAtHead(self, val: int) -> None:
self.dummy_head.next = ListNode(val, self.dummy_head.next)
self.size += 1
def addAtTail(self, val: int) -> None:
current = self.dummy_head
while current.next:
current = current.next
current.next = ListNode(val)
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = ListNode(val, current.next)
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = current.next.next
self.size -= 1
--------------------- c++ ----------------------
class MyLinkedList {
public:
// 定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
// 初始化链表
MyLinkedList() {
_dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
_size = 0;
}
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
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* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
// 在链表最后面添加一个节点
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则在头部插入节点
void addAtIndex(int index, int val) {
if(index > _size) return;
if(index < 0) index = 0;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
void deleteAtIndex(int index) {
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--;
}
// 打印链表
void printLinkedList() {
LinkedNode* cur = _dummyHead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode* _dummyHead;
};
(3) 206 翻转链表
题目描述:反转一个单链表。
递归法:head.next.next=head
需要注意的是 head的下一个节点必须指向 ∅。如果忽略了这一点,链表中可能会产生环。
迭代法:我前面是谁,我是谁,我后面是谁。
--------------------- python ----------------------
递归法:
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
newhead=self.reverseList(head.next)
head.next.next=head
head.next=None
return newhead
以1234为例:
1、newhead=re(2);2、调用re(2) newhead=re(3);3、调用re(3) newhead=re(4);4、调用re(4)这时head.next=None,返回4;4、跟步骤3对应,newhead=4,继续执行,3.next.next=3,得到4-3;5、跟步骤2 对应 执行re(3)后面部分 得到4-3-2--------省略。
迭代法:
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
pre=None
cur=head
while cur:
nex=cur.next
cur.next=pre
pre=cur
cur=nex
return pre
附一个next()用法:
my_list = [1, 2, 3]
my_iterator = iter(my_list)
print(next(my_iterator)) # 输出: 1
print(next(my_iterator)) # 输出: 2
print(next(my_iterator)) # 输出: 3
# print(next(my_iterator)) # 这将引发 StopIteration 异常
--------------------- c++ ----------------------
递归法:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==nullptr || head->next==nullptr) return head;
ListNode* newhead=reverseList(head->next);
head->next->next=head;
head->next=nullptr;
return newhead;
}
};
迭代法:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==nullptr||head->next==nullptr) return head;
ListNode* pre=nullptr;
while(head){
ListNode*next=head->next;
head->next=pre;
pre=head;
head=next;
}
return pre;
}
};
(4) 24 两两交换链表中的节点
题目描述:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
Tips:设置虚拟结点会方便很多。
--------------------- python ----------------------
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
prehead=ListNode(0)
prehead.next=head
cur=prehead
while cur.next and cur.next.next:
tmp=cur.next
tmp1=cur.next.next.next
cur.next=cur.next.next
cur.next.next=tmp
cur.next.next.next=tmp1
cur=cur.next.next
return prehead.next
--------------------- c++ ----------------------
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr) {
ListNode* tmp = cur->next; // 记录临时节点
ListNode* tmp1 = cur->next->next->next; // 记录临时节点
cur->next = cur->next->next; // 步骤一
cur->next->next = tmp; // 步骤二
cur->next->next->next = tmp1; // 步骤三
cur = cur->next->next; // cur移动两位,准备下一轮交换
}
ListNode* result = dummyHead->next;
delete dummyHead;
return result;
}
};
(5) 19 删除链表的倒数第N个节点
题目描述:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。(尝试一趟扫描实现)
Tips:如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
--------------------- python ----------------------
--------------------- c++ ----------------------
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
// ListNode* slow=pre,*fast=pre;
// ListNode* slow=pre,fast=pre; 不行
while(n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
// ListNode *tmp = slow->next; C++释放内存的逻辑
// slow->next = tmp->next;
// delete tmp;
return dummyHead->next;
}
};
(6) 160 链表相交
题目描述:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
Tips:双指针遍历。
--------------------- python ----------------------
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
if not headA or not headB:
return null
p1=headA
p2=headB
while p1 != p2:
p1=p1.next if p1 else headB
p2=p2.next if p2 else headA
return p1
--------------------- c++ ----------------------
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==nullptr && headB==nullptr) return NULL;
ListNode *a=headA;
ListNode *b=headB;
while(a!=b){
a!=nullptr? a=a->next :a=headB;
b!=nullptr? b=b->next :b=headA;
}
return a;
}
};
(7) 141 环形链表
题目描述:给你一个链表的头节点 head ,判断链表中是否有环。
--------------------- python ----------------------
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if not head or not head.next:
return False
slow=fast=head
while fast and fast.next:
slow=slow.next
fast=fast.next.next
if fast==slow:
return True
return False
--------------------- c++ ----------------------
class Solution {
public:
bool hasCycle(ListNode *head) {
if (not head) return false;
ListNode* fast=head;
ListNode* slow=head;
while(fast && fast->next){
slow=slow->next;
fast=fast->next->next;
if(fast==slow){
return true;
}
}
return false;
}
};
(8) 142 环形链表2
题目描述:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
相较于141是要找到链表环的入口。 》》》 推导原理
--------------------- python ----------------------
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
f=head
s=head
while f and f.next:
s=s.next
f=f.next.next
if s==f:
index1=f
index2=head
while index1!=index2:
index1=index1.next
index2=index2.next
return index1
return None
--------------------- c++ ----------------------
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 nullptr;
}
};