链表基础系列1
- 链表基础
- 移除链表元素
- 203 移除链表元素
- 代码随想录的代码
- 707 设计链表
- 我的代码(错误太多,一致debug,没有用虚拟头,不想写了,是未通过的代码)
- 代码随想录的代码
- 小记:双链表好复杂,要仔细看。
- 206 反转链表
- 代码随想录的思路解答
- 没什么思路,直接看的答案
- 代码随想录的代码
- 感悟
- 参考
链表基础
1、链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。链表的入口节点称为链表的头结点也就是head。
2、链表类型:单链表,双链表,循环链表。
双链表:单链表中的指针域只能指向节点的下一个节点。双链表的每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。双链表既可以向前查询也可以向后查询。
循环链表:链表首尾相连。循环链表可以用来解决约瑟夫环问题。
3、链表的存储方式:数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。链表是通过指针域的指针链接在内存中各个节点。所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
4、链表的定义:链表节点的定义,很多同学在面试的时候都写不好。这是因为平时在刷leetcode的时候,链表的节点都默认定义好了,直接用就行了,所以同学们都没有注意到链表的节点是如何定义的。而在面试的时候,一旦要自己手写链表,就写的错漏百出。
5、链表与数组的对比。
6、Python自定义链表:
class ListNode:
def __init__(self, val, next=None):
self.val = val
self.next = next
7、Java和Python语言都有自己的自动回收内存机制,所以在对链表进行操作时,不需要手动释放内存。
移除链表元素
移除操作要注意的就是删除头结点的情况。要单独写一段逻辑来处理移除头结点的情况。
这里就涉及如下链表操作的两种方式:
1、直接使用原来的链表来进行删除操作。
2、设置一个虚拟头结点在进行删除操作。
1:移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。
2:可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。来看看如何设置一个虚拟头。依然还是在这个链表中,移除元素1。最后在题目中,return 头结点的时候,别忘了 return dummyNode->next;, 这才是新的头结点
203 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例2:
输入:head = [], val = 1
输出:[]
示例3:
输入:head = [7,7,7,7], val = 7
输出:[]
小记:移除元素的逻辑是很简单的,但是这是我第一次在Python中用到链表,相关的语句语法不是很熟悉,第一题就直接看代码随想录给出的代码了。
代码随想录的代码
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummyHead = ListNode(0, head)
cur = dummyHead
while cur.next != None:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return dummyHead.next
此代码和力扣的示例代码一致。
707 设计链表
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList 类:
MyLinkedList() 初始化 MyLinkedList 对象。
int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
示例:
输入
[“MyLinkedList”, “addAtHead”, “addAtTail”, “addAtIndex”, “get”, “deleteAtIndex”, “get”]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]
解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 3
我的代码(错误太多,一致debug,没有用虚拟头,不想写了,是未通过的代码)
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self.head = ListNode(None,None)
def get(self, index: int) -> int:
cur = self.head
count = 0
if index == 0:
return cur.val
else:
while cur.next != None:
if count == index :
return cur.val
cur = cur.next
count = count + 1
if count == index :
return cur.val
return -1
def addAtHead(self, val: int) -> None:
if self.head.val == None:
self.head = ListNode(val,None)
else :
add_head = ListNode(val,self.head)
self.head = add_head
def addAtTail(self, val: int) -> None:
if self.head.val == None:
self.head = ListNode(val,None)
else :
cur = self.head
while cur.next != None:
cur = cur.next
add_tail = ListNode(val,None)
cur.next = add_tail
def addAtIndex(self, index: int, val: int) -> None:
add_n = ListNode(val,None)
cur = self.head
if index == 0:
add_n.next = self.head
self.head = add_n
return
count = 0
while cur.next != None:
if count == index-1 :
temp = cur.next
cur.next = add_n
add_n.next = temp
cur = cur.next
count = count + 1
if count == index-1:
cur.next = add_n
def deleteAtIndex(self, index: int) -> None:
cur = self.head
count = 0
while cur.next != None:
if index == 0 :
self.head = self.head.next
break
elif count == index-1 :
cur.next = cur.next.next
break
cur = cur.next
count = count + 1
代码随想录的代码
(版本一)单链表法
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
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
(版本二)双链表法
class ListNode:
def __init__(self, val=0, prev=None, next=None):
self.val = val
self.prev = prev
self.next = next
class MyLinkedList:
def __init__(self):
self.head = None
self.tail = None
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
if index < self.size // 2:
current = self.head
for i in range(index):
current = current.next
else:
current = self.tail
for i in range(self.size - index - 1):
current = current.prev
return current.val
def addAtHead(self, val: int) -> None:
new_node = ListNode(val, None, self.head)
if self.head:
self.head.prev = new_node
else:
self.tail = new_node
self.head = new_node
self.size += 1
def addAtTail(self, val: int) -> None:
new_node = ListNode(val, self.tail, None)
if self.tail:
self.tail.next = new_node
else:
self.head = new_node
self.tail = new_node
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
if index == 0:
self.addAtHead(val)
elif index == self.size:
self.addAtTail(val)
else:
if index < self.size // 2:
current = self.head
for i in range(index - 1):
current = current.next
else:
current = self.tail
for i in range(self.size - index):
current = current.prev
new_node = ListNode(val, current, current.next)
current.next.prev = new_node
current.next = new_node
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
if index == 0:
self.head = self.head.next
if self.head:
self.head.prev = None
else:
self.tail = None
elif index == self.size - 1:
self.tail = self.tail.prev
if self.tail:
self.tail.next = None
else:
self.head = None
else:
if index < self.size // 2:
current = self.head
for i in range(index):
current = current.next
else:
current = self.tail
for i in range(self.size - index - 1):
current = current.prev
current.prev.next = current.next
current.next.prev = current.prev
self.size -= 1
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
小记:双链表好复杂,要仔细看。
206 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
代码随想录的思路解答
1、双指针法:首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
时间复杂度: O(n)
空间复杂度: O(1)
2、递归法
代码思路和双指针法一致,只不过写法变了。
时间复杂度: O(n), 要递归处理链表的每个节点
空间复杂度: O(n), 递归调用了 n 层栈空间
没什么思路,直接看的答案
代码随想录的代码
(版本一)双指针法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
cur = head
pre = None
while cur:
temp = cur.next # 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur.next = pre #反转
#更新pre、cur指针
pre = cur
cur = temp
return pre
(版本二)递归法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
return self.reverse(head, None)
def reverse(self, cur: ListNode, pre: ListNode) -> ListNode:
if cur == None:
return pre
temp = cur.next
cur.next = pre
return self.reverse(temp, cur)
感悟
此题,反转链表,没有思路的原因:为什么要一直盯着链表的尾结点去看呢?总想着要先把尾结点放在头结点前,这样就涉及到O(n^2)的复杂度了!所以一直没有思路!
直接从前向后,反转next不就好了吗,最后只要return最后的结点就好了!不要被从左向右的主观方向迷惑了!
参考
以上部分内容来自:力扣官网,代码随想录