数据结构之链表

一.删除排序链表的重复元素

其实就是遍历元素,如果碰到下一个相同的,就指向下下一个,如果不同的话,就指向下一个

如果要删除链表的重复元素,可以分成两步走,一步是对链表进行排序,另一步就是相当于删除排序数组的重复元素

如果要删除链表的重复元素,而不改变数据,暴力解法,遇到相同的元素,从头开始就执行删除操作,时间复杂度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。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值