python_7_链表练习+考虑时空复杂

技巧

  • 使用容器(数组、哈希表等)
  • 快慢指针

1 快慢指针

1.1 链表节点

(1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点

class Node:
    def __init__(self,value):
        self.value = value
        self.next = None

class Linklist:
    def __init__(self):
        self.head = Node(None)
        self.head.next = None

    def pushlist(self,value):    # 尾插法
        node = Node(value)
        node.next = None
        node_p = self.head
        while node_p.next is not None:
            node_p = node_p.next
        node_p.next = node

    def bianli(self):
        if self.head is None:
            return
        else:
            node_p = self.head.next
            while node_p is not None:
                print(node_p.value)
                node_p = node_p.next

    def lenght(self,list):
        node_p = self.head
        count = 0
        while node_p is not None:
            node_p = node_p.next
            count = count + 1
        return count

    def getzhongdian(self,list):
        if list.head == None or list.head.next == None or list.head.next.next == None:
            return list.head.next.value
        length = self.lenght(list) - 1      # 知道链表长度,不减1,它算的是带头节点的链表长度
        mid = int(length / 2)
        if length % 2 == 1:         # 如果是奇数,返回中点
            count = 0
            current = self.head
            while count <= mid:
                current = current.next
                count = count + 1     # 循环一次
        else:
            count = 0
            current = self.head
            while count < mid:
                current = current.next
                count = count + 1  # 循环一次
        mid_value = current.value
        return mid_value

class Linkcaozuo:


L = Linklist()
L.pushlist(1)
L.pushlist(2)
L.pushlist(3)
L.pushlist(4)
# L.pushlist(5)
print(L.getzhongdian(L))

(2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点

    def getzhongdian(self,list):
        if list.head == None or list.head.next == None:
            return list.head.next.value
        length = self.lenght(list) - 1      # 知道链表长度
        mid = int(length / 2)
        if length % 2 == 1:         # 如果是奇数,返回中点
            count = 0
            current = self.head
            while count <= mid:
                current = current.next
                count = count + 1     # 循环一次
        else:                        # 如果是偶数返回下中点
            count = 0
            current = self.head
            while count < mid:
                current = current.next
                count = count + 1  # 循环一次
        mid_value = current.next.value
        return mid_value

(3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个

    def getzhongdian(self,list):
        if list.head == None or list.head.next == None or list.head.next.next == None:
            return 0
        length = self.lenght(list) - 1      # 知道链表长度
        mid = int(length / 2)
        if length % 2 == 1:         # 如果是奇数,返回中点前一个
            count = 0
            current = self.head
            while count < mid:
                current = current.next
                count = count + 1     # 循环一次
        else:                        # 如果是偶数返回上中点前一个
            count = 0
            current = self.head
            while count < mid - 1:
                current = current.next
                count = count + 1  # 循环一次
        mid_value = current.value
        return mid_value

(4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个

    def getzhongdian(self,list):
        if list.head == None or list.head.next == Nonereturn 0
        length = self.lenght(list) - 1      # 知道链表长度
        mid = int(length / 2)
        if length % 2 == 1:         # 如果是奇数,返回中点前一个
            count = 0
            current = self.head
            while count < mid:
                current = current.next
                count = count + 1     # 循环一次
        else:                        # 如果是偶数返回下中点前一个
            count = 0
            current = self.head
            while count < mid:
                current = current.next
                count = count + 1  # 循环一次
        mid_value = current.value
        return mid_value

下面开始优化代码:使用快慢指针

(1)

    def getzhongdian(self,list):
        if list.head.next is None or list.head.next.next is None:
            return list.head.next.value
        slow = list.head.next.next                      # 慢指针
        fast = list.head.next.next.next					# 快指针
        while fast.next is not None and fast.next.next is not None:
            slow = slow.next
            fast = fast.next.next
        return slow.value

(2)

    def getzhongdian(self,list):
        if list.head.next is None or list.head.next.next is None:
            return list.head.next.value
        slow = list.head.next.next
        fast = list.head.next.next
        while fast.next is not None and fast.next.next is not None:
            slow = slow.next
            fast = fast.next.next
        return slow.value

(3)

    def getzhongdian(self,list):
        if list.head.next is None or list.head.next.next is None:
            return list.head.next.value
        slow = list.head.next                 # 慢指针跑一个
        fast = list.head.next.next.next       # 快指针跑两个
        while fast.next is not None and fast.next.next is not None:
            slow = slow.next
            fast = fast.next.next
        return slow.value

(4)

    def getzhongdian(self,list):
        if list.head.next is None or list.head.next.next is None:
            return 0
        if list.head.next.next.next is None:
            return list.head.next.value
        slow = list.head.next                 # 慢指针跑一个
        fast = list.head.next.next            # 快指针跑两个
        while fast.next is not None and fast.next.next is not None:
            slow = slow.next
            fast = fast.next.next
        return slow.value

1.2 判断是否回文

1.2.1变成数组形式(这个很简单)
    def anagrams(self,list):
        if self.head is None:
            return 0
        num = []
        cur = self.head
        while cur is not None:
            value = cur.value
            cur = cur.next
            num.append(value)             # 将链表的数放入数组内
        i = 0
        j = len(num)-1
        flag = 0
        if i == j:                # 一个数字也算回文
            return True
        while i < j:
            if num[i] == num[j]:
                flag = 1
            else:
                flag = 0
                break               # 只要不相等一次,就不是回文
            i = i + 1
            j = j - 1
        if flag == 1:
            print("回文")
        else:
            print("不是回文")
1.2.2 利用栈结构
    def isPalind(self,list):
        stack = Stack()
        cur = list.head
        while cur is not None:
            stack.push(cur.value)
            cur = cur.next
        while list.head is not None:
            if list.head.value != stack.pop():
                return False
            list.head = list.head.next
        return True
1.2.3 再优化:利用快慢指针,不开辟空间
       def isPalind(self, list):         # 回文判断
        if list.head == None or list.head.next == None:
            return True
        n1 = list.head
        n2 = list.head
        while n2.next != None and n2.next.next != None: # 保证长度>=3个
            n1 = n1.next            # n1->mid
            n2 = n2.next.next       # n2->end
        # 奇数个n1来到中点,偶数n1到上中点
        n2 = n1.next                # n2指向了右边第一个节点
        n1.next = None              # mid.next -> NONE 中间节点指向空
        while n2 != None:           # 右边翻转
            n3 = n2.next            # 保存下一个节点,n3记一下位置
            n2.next = n1            # 右边的下一个节点指向中间
            n1 = n2                 # n1指向已经反转的节点
            n2 = n3
        n3 = n1                     # n3-> 保存最后一个节点,n3记一下最后一个节点
        n2 = list.head              # n2-> 左边的第一个节点
        res = True
        # n2,n1往中间走
        while n1 != None and n2 != None:   # n1在最右边,n2在最左边
            if n1.value != n2.value:
                res = False
                break
            n1 = n1.next
            n2 = n2.next
        n1 = n3.next
        n3.next = None
        while n1 != None:      # 再将链表转回来
            n2 = n1.next
            n1.next = n3
            n3 = n1
            n1 = n2
        return res

2 单链表分左中右区域(1)

在这里插入图片描述
在使用荷兰国旗问题解决时遇到了返回头节点只输出了一个值
在这里插入图片描述

错误原因 return nodeArr[0] ,不能用return返回嘛?不能返回一串链表,实际缺只能返回一个节点??

    def listpartition(self,pivot):  # pivot 是指定的分隔数
        if self.head is None:           # 链表为空返回头节点
            return self.head
        nodeArr = []                # 创建一个数组
        i = 0
        cur = self.head
        while cur is not None:      # 将节点全部放入数组
            nodeArr.append(cur)     # nodeArr是数组,存放的是节点
            cur = cur.next
        self.partition(nodeArr,0,len(nodeArr)-1,pivot)     # 对节点数组进行快速排序
        for i in range(1,len(nodeArr)):
            nodeArr[i-1].next = nodeArr[i]       # 第一个节点与下一个节点相连
        nodeArr[i].next = None
        current = nodeArr[0]
        while current is not None:      # 证明了节点之间是相连接的
            print(current.value)
            current = current.next

        return nodeArr[0]                           # 返回头节点

探讨return就此过去,耗费时间过长

修改正确

class Node:
    def __init__(self,value):
        self.value = value
        self.next = None

class Linklist:
    def __init__(self):
        self.head = None

    def pushlist(self,value):    # 尾插法
        node = Node(value)
        node.next = None
        if self.head is None:
            self.head = node
        else:
            cur = self.head
            while cur.next is not None:
                cur = cur.next
            cur.next = node

    def bianli(self):
        if self.head is None:
            return
        else:
            node_p = self.head
            while node_p is not None:
                print(node_p.value)
                node_p = node_p.next

    def lenght(self):
        if self.head is None:
            return 0
        node_p = self.head
        count = 1
        while node_p is not None:
            node_p = node_p.next
            count = count + 1
        return count

    def listpartition(self,pivot):  # pivot 是指定的分隔数
        if self.head is None:           # 链表为空返回头节点
            return self.head
        nodeArr = []                # 创建一个数组
        i = 0
        cur = self.head
        while cur is not None:      # 将节点全部放入数组
            nodeArr.append(cur)     # nodeArr是数组,存放的是节点
            cur = cur.next
        self.partition(nodeArr,0,len(nodeArr)-1,pivot)     # 对节点数组进行快速排序
        for i in range(1,len(nodeArr)):
            nodeArr[i-1].next = nodeArr[i]       # 第一个节点与下一个节点相连
        nodeArr[i].next = None
        self.head = nodeArr[0]                     # 接受返回头节点


    def swap(self,arr, i, j):
        tmp = arr[i]
        arr[i] = arr[j]
        arr[j] = tmp


    def partition(self,arr,L,R,pri):
        if L > R:
            return -1
        if L == R:
            return L
        less = L - 1  # 小于区域右边界,不包括左边第一个数
        more = R + 1  # 大于区域左边界,不包括右边第一个数
        index = L                             # 左边第一个数
        while index < more:                   # 左右两个指针没有相遇时
            if arr[index].value == pri:       # 以pri作分割
                index = index + 1
            elif arr[index].value < pri:
                less = less + 1               # 小于区域向右扩一个位置
                self.swap(arr, index, less)   # 把小值和小于区域的数交换
                index = index + 1             # index移动到下一个
            else:                             # 第一次,如果右边第一个数大于左边
                more = more - 1               # 大于区域向左扩一个位置,more = 5
                self.swap(arr, index, more)   # R[5] 与 左边第一个数交换


L = Linklist()
L.pushlist(10)            # 长度为9
L.pushlist(5)
L.pushlist(4)
L.pushlist(1)
L.listpartition(4)     # 以4分割
L.bianli()

对链表返回节点做实验—>

优化 对链表内的数做快排

class Node:
    def __init__(self,value):
        self.value = value
        self.next = None

class Linklist:
    def __init__(self):
        self.head = None

    def pushlist(self,value):    # 尾插法
        node = Node(value)
        node.next = None
        if self.head is None:
            self.head = node
        else:
            cur = self.head
            while cur.next is not None:
                cur = cur.next
            cur.next = node

    def bianli(self):
        if self.head is None:
            return
        else:
            node_p = self.head
            while node_p is not None:
                print(node_p.value)
                node_p = node_p.next

    def lenght(self,list):
        if self.head is None:
            return 0
        node_p = self.head
        count = 1
        while node_p is not None:
            node_p = node_p.next
            count = count + 1
        return count

    def listpartition(self,list):  
        if list.head is None:       # 链表为空返回头节点
            return list.head
        nodeArr = []                # 创建一个数组
        i = 0
        cur = list.head
        while cur is not None:      # 将节点全部放入数组
            nodeArr.append(cur)     # nodeArr是数组,存放的是节点
            cur = cur.next
        self.quickSort(nodeArr)     # 对节点数组进行快速排序


        for i in range(1,len(nodeArr)):
            nodeArr[i-1].next = nodeArr[i]       # 第一个节点与下一个节点相连
        nodeArr[i-1].next = None
        return nodeArr[0]                        # 返回头节点


    def swap(self,arr, i, j):
        tmp = arr[i]
        arr[i] = arr[j]
        arr[j] = tmp

    def quickSort(self,nodeArr):
        if nodeArr == None or len(nodeArr) < 2:
            return
        self.process(nodeArr,0,len(nodeArr)-1)

    def process(self,arr,L,R):
        if L >= R:
            return
        equalArea = self.partition(arr, L, R)  # equalArea接受的是中间数的位置
        self.process(arr, L, equalArea[0] - 1)  # equalArea[0] - 1 是小于区域的最后一个数
        self.process(arr, equalArea[1] + 1, R)  # equalArea[1] + 1 是大于区域的第一个数


    def partition(self,arr,L,R):
        if L > R:
            return [-1, -1]
        if L == R:
            return [L, R]
        less = L - 1                    # 小于区域右边界,不包括左边第一个数
        more = R                        # 大于区域左边界,[6],more = 6,包括了右边第一个数,要固定右边第一个数
        index = L                       # 左边第一个数
        while index < more:             # 左右两个指针没有相遇时
            if arr[index].value == arr[R].value:    # 以arr[R]作分割
                index = index + 1
            elif arr[index].value < arr[R].value:
                less = less + 1         # 小于区域向右扩一个位置
                self.swap(arr, index, less)  # 把小值和小于区域的数交换
                index = index + 1       # index移动到下一个
            else:                       # 第一次,如果右边第一个数大于左边
                more = more - 1         # 大于区域向左扩一个位置,more = 5,先将R[6]固定在最后一个位置上
                self.swap(arr, index, more)  # R[5] 与 左边第一个数交换,将R[6]数固定在最后一个位置上
        self.swap(arr, more, R)              # 将大于区域的第一个数和R(固定的数)位置上交换,接触R位置固定
        return [less + 1, more]         # 返回等于区域的位置,返回大小为2的数组,第一个数是等于区域最左边的数,第二个数是等于区域最右边的数

L = Linklist()
L.pushlist('1')
L.pushlist('2')
L.pushlist('3')
L.pushlist('3')
L.pushlist('2')
L.pushlist('1')
L.listpartition(L)
L.bianli()

3 完成链表复制,返回头节点

在这里插入图片描述
next无环
在这里插入图片描述

3.1 使用哈希表(字典)

class Node:
    def __init__(self,value):
        self.value = value
        self.next = None
        self.rand = None

class Linklist:
    def __init__(self):
        self.head = None
        self.map = dict()

    def copylist(self):
        cur = self.head
        while cur is not None:
            # 老节点克隆新节点放到map去
            new_node = Node(cur.value)
            self.map.update({cur:new_node})    # 往字典里面放值
            cur = cur.next
        # 老节点新节点已经克隆好了,新关系已存在map里
        cur = self.head
        while cur is not None:
            # cur 老节点   , 因为cur是key,get是value
            # map.get(cur) 新节点
            # 得到当前节点的value,也就是新节点,新节点的next指向老节点下一个对应的新节点
            self.map.get(cur).next = self.map.get(cur.next)
            self.map.get(cur).rand = self.map.get(cur.rand)
            cur = cur.next
        # 返回给self.head的克隆头节点
        self.head = self.map.get(self.head)

3.2 不用哈希表

在这里插入图片描述
人为构造关系

class Node:
    def __init__(self,value):
        self.value = value
        self.next = None
        self.rand = None

class Linklist:
    def __init__(self):
        self.head = None
        self.map = dict()

    def copylist(self):
        if self.head == None:
            return None
        cur = self.head
        # 复制节点,并且将复制节点放到中间
        # 1->2
        # 1->1'>2
        while cur is not None:
            # cur 老节点
            next = cur.next
            cur.next = Node(cur.value)
            cur.next.next = next    # cur将新节点指向下一个老节点
            cur = next              # cur到下一个老节点
        cur = self.head
        # 设置克隆的rand指针
        # 1->1'->2->2'
        while cur is not None:
            # cur 新节点
            # cur.next 新复制的节点
            next = cur.next.next   # next指向下一个老节点,中间是有新节点的
            curCopy = cur.next     # 指向第一个新节点
            if cur.rand is not None:       # 如果老节点不是空
                # 新节点的rand就指向老节点rand指向的下一个,因为位置关系是确定的
                curCopy.rand = cur.rand.next
            else:
                curCopy.rand = None
            cur = next
        res = self.head.next
        cur = self.head
        # 分离新旧节点
        while cur is not None:
            next = cur.next.next    # 指向旧节点的第二个
            curCopy = cur.next      # 指向新节点
            cur.next = next         # 第一个旧节点断开与第一个新节点链接,链接到第二个旧节点
            if next is not None:
                curCopy.next = next.next   # 新节点的下一个为第二个旧节点的下一个新节点
            else:
                curCopy.next = None
            cur = next
        self.head = res


4 两个头节点相交

4.1 情况一:单链表有环找交点

在这里插入图片描述
使用字典找环入口处
在这里插入图片描述
使用快慢指针找环入口处
在这里插入图片描述

def getLoopNode(list):  # 找到链表第一个入环节点,如果无环,返回None
    if list.head == None or list.head.next == None or list.head.next.next == None: # 2个节点没有环,保证节点数量大于3
        return None
    n1 = list.head.next         # 慢指针
    n2 = list.head.next.next    # 快指针
    while n1 != n2:
        if n2.next == None or n2.next.next == None:
            return None
        n2 = n2.next.next
        n1 = n1.next
    n2 = list.head          # 快指针回到头节点
    while n1 != n2:
        n1 = n1.next
        n2 = n2.next
    return n1

L = Linklist()
L.pushlist(10)            # 长度为9
L.pushlist(5)
L.pushlist(4)
L.pushlist(1)
getLoopNode(L)

4.2 情况二:单链表无环找交点

两条链表相交之后剩下的只有公共部分了,因为next只能指向一个
所以两个链表最后一个节点地址相同,说明已经相交

# 如果两个链表都无环,返回第一个相交节点,如果不相交,返回None
def noLoop(list1,list2):
    if list1.head == None or list2.head == None:
        return None
    cur1 = list1.head
    cur2 = list2.head
    n = 0
    while cur1.next != None:    # 让cur1指针指向尾节点
        n = n + 1
        cur1 = cur1.next
    while cur2.next != None:    # 让cur2指针指向尾节点
        n = n - 1
        cur2 = cur2.next
    if cur1 != cur2:            # 如果尾节点不相交,返回None
        return None
    # n值 ,链表1长度减去链表2长度的值
    if n > 0:              # 谁长,谁的头变成cur1,重定向
        cur1 = list1.head
    else:
        cur1 = list2.head
    if cur1 == list1.head:  # 谁短,谁的头变成cur2
        cur2 = list2.head
    else:
        cur2 = list1.head
    n = abs(n)
    while n != 0:
        n = n - 1     # 长链表先走差值数
        cur1 = cur1.next
    while cur1 != cur2:
        cur1 = cur1.next
        cur2 = cur2.next
    return cur1

4.3 情况三:相交直线+环

loop1 loop2 是相交的第一个节点地址
在这里插入图片描述

# 两个有环链表,返回第一个相交节点,如果不相交返回None
def bothLoop(list1,loop1,list2,loop2):
    if loop1 == loop2:         # loop是第一个相交节点地址
        cur1 = list1.head
        cur2 = list2.head
        n = 0
        while cur1 != loop1:   # 遇到loop1停
            n = n + 1
            cur1 = cur1.next
        while cur2 != loop2:   # 遇到loop2停
            n = n - 1
            cur2 = cur2.next
        if n > 0:
            cur1 = list1.head
        else:
            cur1 = list2.head
        if cur1 == list1.head:  # 谁短,谁的头变成cur2
            cur2 = list2.head
        else:
            cur2 = list1.head
        n = abs(n)
        while n != 0:
            n = n - 1  # 长链表先走差值数
            cur1 = cur1.next
        while cur1 != cur2:
    else:
        cur1 = loop1.next
        while cur1 != loop1:
            if cur1 == loop2:
                return loop1     # 返回loop1 loop2都行,都是第一个相交的节点
            cur1 = cur1.next
        return None           # 否则不相交


4.4 主函数

def getInters(list1,list2):
    if list1.head == None or list2.head == None:
        return None
    loop1 = getLoopNode(list1.head)
    loop2 = getLoopNode(list2.head)
    if loop1 == None and loop2 == None:
        return noLoop(list1.head,list2.head)
    if loop1 != None and loop2 != None:
        return bothLoop(list1.head,loop1,list2.head,loop2)
    return None
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值