一.删除排序链表的重复元素
其实就是遍历元素,如果碰到下一个相同的,就指向下下一个,如果不同的话,就指向下一个
如果要删除链表的重复元素,可以分成两步走,一步是对链表进行排序,另一步就是相当于删除排序数组的重复元素
如果要删除链表的重复元素,而不改变数据,暴力解法,遇到相同的元素,从头开始就执行删除操作,时间复杂度O(n^2),可以进行优化的
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head:
return None
cur_List = head
while cur_List.next:
if cur_List.val == cur_List.next.val:
cur_List.next = cur_List.next.next
else:
cur_List = cur_List.next
return head
二.链表环的问题
判断链表中是否有环 ----- 有关单链表中环的问题这篇文章写的很好,可以看一看,里面把所有环的问题都列举了,可以看一看
2.1判断有环无环
对于这个问题我们可以采用“快慢指针”的方法。
就是有两个指针fast和slow,开始的时候两个指针都指向链表头head,然后在每一步操作中slow向前走一步即:slow = slow->next,而fast每一步向前两步即:fast = fast->next->next。由于fast要比slow移动的快,如果有环,fast一定会先进入环,而slow后进入环。当两个指针都进入环之后,经过一定步的操作之后二者一定能够在环上相遇,并且此时slow还没有绕环一圈,也就是说一定是在slow走完第一圈之前相遇。
def exitLoop(LList):
p1 = p2 = LList
while p2 and p2.pnext: #当链表为空或者只有一个结点时,就不执行循环体里的程序,返回False
p1 = p1.pnext
p2 = p2.pnext.pnext
if p1 == p2:
return True
return False
2.2环入口
在这里我们可以推导出来,从链表起点head开始到入口点的距离a,与从slow和fast的相遇点(如图)到入口点的距离相等。
因此我们就可以分别用一个指针(ptr1, prt2),同时从head与slow和fast的相遇点出发,每一次操作走一步,直到ptr1 == ptr2,此时的位置也就是入口点!
def findbeginofloop(head):#判断是否为环结构并且查找环结构的入口节点
slowPtr = head #将头节点赋予slowPtr
fastPtr = head #将头节点赋予fastPtr
loopExist =False #默认环不存在,为False
if head == None: #如果头节点就是空的,那肯定就不存在环结构
return False
while fastPtr.next != None and fastPtr.next.next != None: #fastPtr的下一个节点和下下个节点都不为空
slowPtr = slowPtr.next #slowPtr每次移动一个节点
fastPtr = fastPtr.next.next #fastPtr每次移动两个节点
if slowPtr == fastPtr : #当fastPtr和slowPtr的节点相同时,也就是两个指针相遇了
loopExist = True
print("存在环结构")
break
if loopExist == True:
slowPtr = head
while slowPtr != fastPtr:
fastPtr = fastPtr.next
slowPtr = slowPtr.next
return slowPtr
print("不是环结构")
return False
2.3环长度
对于这个问题,我这里有两个思路(肯定还有其它跟好的办法):
思路1:记录下相遇节点存入临时变量tempPtr,然后让slow(或者fast,都一样)继续向前走slow = slow -> next;一直到slow == tempPtr; 此时经过的步数就是环上节点的个数;
思路2: 从相遇点开始slow和fast继续按照原来的方式向前走slow = slow -> next; fast = fast -> next -> next;直到二者再次项目,此时经过的步数就是环上节点的个数 。
第一种思路很简单,其实就是一次遍历链表的环,从而统计出点的个数,没有什么可以详细解释的了。
对于第二种思路,我们可以这样想,结合上面的分析,fast和slow没一次操作都会使得两者之间的距离较少1。我们可以把两者相遇的时候看做两者之间的距离正好是整个环的长度r。因此,当再次相遇的时候所经过的步数正好是环上节点的数目。和上面的代码基本是一样的
# 链表结点类
class Node():
def __init__(self, item=None):
self.item = item # 数据域
self.next = None # 指针域
# 链表类,生成链表以及定义相关方法
class LinkList():
def __init__(self):
self.head = None
# 生成链表,这里使用list来生成
def create(self, item):
self.head = Node(item[0])
p = self.head
for i in item[1:]:
p.next = Node(i)
p = p.next
# 遍历显示
def print(self):
p = self.head
while p != None:
print(p.item, end=' ')
p = p.next
print()
# 根据索引取值
def getItem(self, index):
p = self.head
count = 0
while count != index:
p = p.next
count += 1
return p.item
# 根据索引设值
def setItem(self, index, item):
p = self.head
count = -1
while count < index-1:
p = p.next
count += 1
p.item = item
# 互换
def swapItem(self, i, j):
t = self.getItem(j)
self.setItem(j, self.getItem(i))
self.setItem(i, t)
def quicksortofloop(self, left, right):
if left < right:
# 初始化
i = left
j = i+1
start = self.getItem(i)
# 大循环条件,j不能超过链表长度
while (j <= right):
# 如果 j 指向的值大于等于基准数字,直接跳过
while (j <= right and self.getItem(j) >= start):
j += 1
# 否则,j 指向的值小于基准,则交换
if (j <= right):
i += 1
self.swapItem(i, j)
self.print()
j += 1
self.swapItem(left, i)
self.quicksortofloop(left, i-1)
self.quicksortofloop(i+1, right)
if __name__ == "__main__":
L = LinkList()
L.create([4, 2, 5, 3, 7, 9, 0, 1])
L.quicksortofloop(0, 7)
L.print()
三.链表的排序问题
1.单向链表的排序
在这里的排序是用快速排序进行实践的,我们首先要得到链表的长度,然后在使用快速排序
// 链表结点类
class Node():
def __init__(self, item=None):
self.item = item // 数据域
self.next = None // 指针域
// 链表类,生成链表以及定义相关方法
class LinkList():
def __init__(self):
self.head = None
// 生成链表,这里使用list来生成
def create(self, item):
self.head = Node(item[0])
p = self.head
for i in item[1:]:
p.next = Node(i)
p = p.next
// 遍历显示
def print(self):
p = self.head
while p != None:
print(p.item, end=' ')
p = p.next
print()
// 根据索引取值
def getItem(self, index):
p = self.head
count = 0
while count != index:
p = p.next
count += 1
return p.item
// 根据索引设值
def setItem(self, index, item):
p = self.head
count = -1
while count < index-1:
p = p.next
count += 1
p.item = item
// 互换
def swapItem(self, i, j):
t = self.getItem(j)
self.setItem(j, self.getItem(i))
self.setItem(i, t)
def quicksortofloop(self, left, right):
if left < right:
// 初始化
i = left
j = i+1
start = self.getItem(i)
// 大循环条件,j不能超过链表长度
while (j <= right):
// 如果 j 指向的值大于等于基准数字,直接跳过
while (j <= right and self.getItem(j) >= start):
j += 1
// 否则,j 指向的值小于基准,则交换
if (j <= right):
i += 1
self.swapItem(i, j)
self.print()
j += 1
self.swapItem(left, i)
self.quicksortofloop(left, i-1)
self.quicksortofloop(i+1, right)
if __name__ == "__main__":
L = LinkList()
L.create([4, 2, 5, 3, 7, 9, 0, 1])
L.quicksortofloop(0, 7)
L.print()
2.双向链表的排序
我们在这里需要知道双向链表的头结点和尾结点,再使用快速排序
from random import randint
class DLinkedNode(object):
def __init__(self, data=None, pre=None, post=None):
self.data = data
self.pre = pre
self.post = post
class DLinkedList(object):
def __init__(self):
self.head = DLinkedNode()
self.tail = DLinkedNode()
self.head.post = self.tail
self.tail.pre = self.head
def build(self, n):
pre = self.head
for _ in range(n):
data = randint(1, 100)
node = DLinkedNode(data, post=self.tail)
self.tail.pre = node
pre.post = node
node.pre = pre
pre = node
return self.head, self.tail
class Solution(object):
def quick_sort(self, head, low, high): #快速排序
if not head or not head.post:
return
if low != high:
p, q = low, high
key = p.data
while p != q:
while p != q and q.data >= key:
q = q.pre
p.data = q.data
while p != q and p.data <= key:
p = p.post
q.data = p.data
p.data = key
if low != p:
self.quick_sort(head, low, p.pre)
if p != high:
self.quick_sort(head, p.post, high)
h, t = DLinkedList().build(10)
curr = h
while curr.post:
print (curr.post.data)
curr = curr.post
print()
while curr.pre:
print (curr.pre.data,)
curr = curr.pre
print()
Solution().quick_sort(h.post, h.post, t.pre)
curr = h
while curr.post:
print (curr.post.data,)
curr = curr.post
四.链表的反转问题
4.1单链表的反转
其实这里主要是三个指针,四句话就可以解决的问题
三个指针pre->cur->next,四句话在程序中
def nonrecurse(head):
if head is None or head.next is None:
return head
pre = None
cur = head
h = head
while cur:
h = cur#这个是为了返回头结点加的一句话
#四句话
next = cur.next
cur.next = pre
pre = cur
cur = next
return h
4.2双向链表的反转和结点的删除
"""删除节点"""
def delete(self, index):
node = self.get(index)
if node:
node.pre.next = node.next
node.next.pre = node.pre
return True
return False
反转双向链表
参考这篇博客:https://blog.csdn.net/junjunba2689/article/details/79987973
三个指针五句话 pre->cur->next
很多地方也是用head来替代cur,都是一样的,看你从那个方向来考虑
next=cur.next
cur.next=pre#->改为<-
cur.pre=next#<-改为->
pre=cur#pre右移
head=next#head右移
五.链表的回文问题
使用一个栈或者两个栈来解决问题
思路1:
看到这个题目时想到的第一个突破口是链表的长度,如果我们知道链表的长度,就可以把链表一分为二,前半部分依次压入栈里,然后再将栈里的元素pop出来,挨个与链表后半部分的结点进行比较,这样的话结果唾手可得。只是该题目只给了结点结构的定义,所以这个长度需要我们自己遍历链表来获取。(所以得先遍历链表得到长度后,在进行比较,在这里借助栈就可以进行逆序了)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def isPalindrome(self, head):
"""
:type head: ListNode
:rtype: bool
"""
res = []
cur = head
while cur:
res.append(cur.val)
cur = cur.next
return res == res[: : -1]
思路2:
快慢指针法
1:首先找到链表长度的一半,用追赶法即可
2.将后面的链表进行转置
3.再判断判断是否回文
class Solution:
def isPalindrome(self, head):
"""
判断一个链表是否是回文的,很自然的想法就是两个指针,一个指针从前往后走,一个指针从后往前走,判断元素值是否相同,这里要分几个步骤来进行求解:
1、找到链表长度的一半,用追赶法,一个指针一次走两步,一个指针一次走一步
2、将后一半数组转置
3、判断链表是否是回文链表
:type head: ListNode
:rtype: bool
"""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
node = None
while slow:
nxt = slow.next
slow.next = node
node = slow
slow = nxt
while node and head:
if node.val != head.val:
return False
node = node.next
head = head.next
return True
总结:
(1)回文序列的长度也可以是奇数,最中间的那个数字是没有其他元素和其对称比较的。因此我们在实现的时候要注意这一点,如果一个序列长度是奇数,我们要跳过其最中间的元素,再进行比较。
(2)思路2占用的时间和空间都稍优于思路1。