1定义:链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
struct ListNode{
int val; #当前节点的值
ListNode *next; #下一个节点的指针
ListNode(int x): val(x), next(NULL){} #构造函数
};
ListNode* head = new ListNode(5); #构造初始化节点
ListNode* head = new ListNode(); 默认构造函数初始化节点
head->val = 5;
class ListNode:
def __init__(self,val,next=None):
self.val = val
self.next = next
mysql的索引原理B+Tree也是这样叶子节点不仅包括了数据还有索引值。
单链表:
双链表:也就是包含两个指针可以指向上下两个节点,可以实现前后查询。
循环链表:很明显就是链表首尾相连成了一个环。
2.存储方式
数组在内存中是连续分布的,(二维在java不是)
链表在内存中不是连续分布的,由其存储的下一个节点的指针就可以看出来,是离散分布的。
想要删除节点,其实也很好理解,就是将上一个节点的指针指向要删除节点 的下一个节点的地址,就实现了删除。注:对于C++而言由于是new出来的所以最好是delete掉,python语言有自己的内存回收机制。
添加节点也很好理解,就是将当前节点的指针指向要添加的节点,然后这个要添加节点的指针指向原本下一个节点的地址。
添加和删除都是O(1),但是查找是O(n)
1.移除链表元素
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = 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:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummyHead = ListNode(val =0)
dummyHead.next = head
cur = dummyHead
while(cur.next is not None):
if cur.next.val == val:
tmp = cur.next
cur.next = cur.next.next
else:
cur = cur.next # 还是需要遍历下去
head = dummyHead.next
return head
2.设计链表(注意:index从0开始)
class MyLinkedList {
public:
// 定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
MyLinkedList() {
_dummyHead = new LinkedNode(0);
_size = 0;
}
int get(int index) {
if(index>(_size-1)||index < 0){
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode* new_head = new LinkedNode(val);
new_head->next = _dummyHead->next;
_dummyHead->next = new_head;
_size++;
}
void addAtTail(int val) {
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 = 0;
if(index>_size) return;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--){
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
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;
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;
};
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
cur = self.dummy_head.next
for i in range(index):
cur = cur.next
if cur == None:
return -1
return cur.val
def addAtHead(self, val: int) -> None:
cur = self.dummy_head
newNode = ListNode(val)
newNode.next = cur.next
cur.next = newNode
self.size += 1
def addAtTail(self, val: int) -> None:
cur = self.dummy_head
while cur.next:
cur = cur.next
cur.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:
pass
else:
cur = self.dummy_head
for i in range(index):
cur = cur.next
cur.next = cur.next.next
self.size -= 1
用python注意点还是比较多的,首先是get方法考虑cur==None
其次就是add和delete方法中index这个等于号要注意,由于index从0开始,对于add而言要想加在最后必须index==self.size,对于delete而言正常是self.size = index -1,如果是self.size=index那么已经超出了范围。
反转链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* temp;
ListNode* cur = head;
ListNode* pre = NULL;
while(cur){
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
};
这个就比较好理解:先用一个pre占位,存储cur节点的下一个节点,将cur节点的下一个节点变成pre(这一步实现反转),之后pre赋值cur,再将temp赋给cur实现遍历。
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
cur = head
pre = None
while(cur):
temp = cur.next
cur.next = pre
pre = cur
cur = temp
return pre
交换链表两两节点:题目还是有点歧义的,并不是不断作相邻交换,而是两个两个一组交换,所以cur = cur->next->next而不是cur=cur->next.
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy_head = new ListNode();
dummy_head->next = head;
ListNode* cur = dummy_head;
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;
}
ListNode* result = dummy_head->next;
delete dummy_head;
return result;
}
};
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummyhead = ListNode()
dummyhead.next = head
cur = dummyhead
while(cur.next != None and cur.next.next != None):
# 由于虚拟头的下一个节点是后面第二个节点,所以第一个节点要保存,
# 第二个节点的下一个节点也要保存
tmp = cur.next
tmp1 = cur.next.next.next
# 开始进行交换
cur.next = cur.next.next
cur.next.next = tmp
cur.next.next.next = tmp1
# 移动cur
cur = cur.next.next
return dummyhead.next
删除链表倒数第n个元素,由于不知道链表到底有多少个元素,所以需要使用双指针,一个快指针一直走到链表结束,另一个慢指针距离链表n+1,当快指针结束时慢指针正好走到倒数第n个元素的前一个。这样就实现了找到,之后删除就是前面的将下一个节点的指针换成下下个节点的指针即可。
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummyhead = ListNode()
dummyhead.next = head
cur = dummyhead
slow = cur
fast = cur
for i in range(n+1):
fast = fast.next
while(fast != None):
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummyhead.next
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode();
dummyhead->next = head;
ListNode* slow = dummyhead;
ListNode* fast = dummyhead;
for(int i=0;i<n+1;i++){
fast = fast->next;
}
while(fast != NULL){
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyhead->next;
}
};
链表相交
对示例1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8'
有点疑问,为什么不是在1相交
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA = 0
lenB = 0
curA = headA
curB = headB
while(curA):
lenA += 1
curA = curA.next
while(curB):
lenB += 1
curB = curB.next
curA = headA
curB = headB
if(lenA<lenB):
lenA, lenB = lenB, lenA
curA, curB = curB, curA
gap = lenA-lenB
for i in range(gap):
curA = curA.next
while(curA):
if(curA==curB):
return curA
else:
curA = curA.next
curB = curB.next
return None
环形链表
class Solution(
public:
ListNode *detectCycle(ListNode *head){
ListNode* fast = head;
ListNode* slow= head;
while(fast!=NULL && fast->next!=NULL){
fast = fast->next->next;
slow = slow->next;
if(slow==fast){
ListNode* con = fast;
slow = head;
while(slow!=con){
slow = slow->next;
con = con->next;
}
return con;
}
return NULL;
}
};
主要还是前期分析,首先 先找到相遇的点,之后通过分析可以知道慢指针一定在入环的第一圈就被追上,所以根据等式可以得到x=z,那么从起点和相遇点开始走,相等的那个点就是入环点。