leetcode算法学习笔记-链表(python版)

b站左老师课程对应链接:4.链表_哔哩哔哩_bilibili

排序的稳定性

相同值的相对位置保持与原来一致,就被视为有稳定性。

对于多属性排序有用,比如要在商品好评率排序的基础上对价格排序,输出在同价格下好评率顺序。 

可以做到有稳定性的排序:冒泡排序(等于时不移动)、插入排序(等于时不移动)、归并排序(相等取左数组;小和问题中,相等先取右数组,无法做到稳定性)。

时间复杂度空间排序稳定性
选择排序O(N**2)O(1)
冒泡排序O(N**2)O(1)
插入排序O(N**2)O(1)
归并排序O(N*LogN)O(N)
快排3.0O(N*LogN)O(logN)
堆排序O(N*LogN)O(1)

一般排序选择快排方法,数据量很多时才使用堆排序,要有稳定性就选择归并排序。目前基于比较的排序算法无法三个方面兼顾。

前三种方案都有对应的方法可以取代,实现还更简单。 

第五个问题:使用快排的步骤中就可以处理0-1标准的问题,但是无法做到稳定性,所以很难做到。

工程上针对不同的样本量对应不同的方法有更快的速度,大样本量时使用快排,小样本量用插入排序,因为插入排序使用的常数操作少,而快排调度数组快,利用各自优点处理不同量级数据。

 哈希表与有序表

 python中类似于HashSet的结构是set,类似于HashMap的结构是dict。python中对于哈希表传递内容都是使用地址引用法,不会像Java与C++对不同类型数据,使用不同的内部传递方法。Python中的哈希表主要通过引用内存地址来获取和存储数据。简而言之,当你在Python中使用字典存储数据时,字典中的每个键都指向相应值的内存地址,而不是直接存储值的副本。

有序表与哈希表的区别在于,有序表会根据key有序的在内存上存在,而哈希表是无序的。因此有序表的key需要可以比较大小,key的大小关系也可以直接推导获得,哈希表则无法得到这些大小关系。有序表的各类操作,时间复杂度O(LogN)。

python库中并没有可以直接使用的有序表结构,只能通过自定义类等其他方式来实现有序表的建立。

单链表

单链表是由节点(Node)组成的每个节点包含数据和指向下一个节点的引用。单链表的优点是可以高效地插入和删除元素,但不支持反向遍历。

双链表是由节点组成,每个节点包含数据、指向下一个节点的引用(next)及指向前一个节点的引用(prev)。双链表不仅可以实现便捷的插入和删除,还支持双向遍历。

反转链表时,是有换头操作的,即头节点从第一个数换到了最后一个数。所以函数f应该是一个head=f(head)的函数。

法1:迭代法

过程描述:1.建立单链表节点类

2.对头节点进行推进,头节点先指向下一节点,获得下一节点。

3.将指针反转

4.将头节点的下一节点值换成前一节点值。

5.头节点变为下一节点,重复2、3、4步骤

时间复杂度:O(n)

空间复杂度:O(1)

class ListNode:
    ##传入不同类型的数据时,将它们变为节点
    def __init__(self, val):
        if isinstance(val, int):
            self.val = val
            self.next = None

        if isinstance(val, list):
            self.val = val[0]
            self.next = None
            head = self
            for i in range(1, len(val)):
                node = ListNode(val[i])
                head.next = node
                head = head.next

def ListReverse(head):
    pre = None
    cur = head

    while cur != None:
        #记录下一节点
        next = cur.next
        #当前节点指向上一节点
        cur.next = pre
        #上一节点更新为当前节点,为了下一轮迭代时,当前节点变为下一节点的前一节点。
        pre = cur
        #当前节点更新为下一节点,为了下一轮迭代能继续之前的操作。
        cur = next

    return pre

if __name__ == '__main__':
    print('###################数组输入#########################')
    arr = [1,2,3,4,5]
    node = ListNode(arr)
    reversed_arr = ListReverse(node)
    while reversed_arr:
        print(reversed_arr.val)
        reversed_arr = reversed_arr.next

    print('###################整数输入#########################')
    head = ListNode(1)
    p1 = ListNode(2)
    p2 = ListNode(3)
    p3 = ListNode(4)
    head.next = p1
    p1.next = p2
    p2.next = p3
    p = ListReverse(head)
    while p:
        print(p.val)
        p = p.next

双链表

class ListNode:
    #构建双链表
    def __init__(self, val):
        if isinstance(val, int):
            self.val = val
            self.next = None
            self.prev = None

        if isinstance(val, list):
            self.val = val[0]
            self.next = None
            self.prev = None
            head = self
            for i in range(1,len(val)):
                node = ListNode(val[i])
                head.next = node
                node.prev = head
                head = head.next

def reverseLink(head):
    cur = head
    new_head = None
    while cur != None:
        cur.next, cur.prev = cur.prev, cur.next
        new_head = cur
        cur = cur.prev
    return new_head

if __name__ == '__main__':
    p0 = ListNode(1)
    p1 = ListNode(2)
    p2 = ListNode(3)
    p3 = ListNode(4)
    p0.next = p1
    p1.next = p2
    p2.next = p3
    p1.prev = p0
    p2.prev = p1
    p3.prev = p2
    reversedInt = reverseLink(p0)
    while reversedInt:
        print(reversedInt.val)
        reversedInt = reversedInt.next

法2:头插法

过程描述:是逐个取出原链表的节点,将它们按顺序插入到新链表的头部。也就是原链表第一个值是新链表的最后一个值、原链表第二个值是新链表的倒二个值...

假设我们有一个原链表:1 -> 2 -> 3 -> 4 -> 5

执行头插法时的步骤如下:

  • 初始状态:新链表为空。

  • 处理节点 1:

    • 创建新节点 1,链接为空。
    • 新链表:1
  • 处理节点 2:

    • 创建新节点 2,链接到新链表的头(当前节点 1)。
    • 新链表:2 -> 1
  • 处理节点 3:

    • 创建新节点 3,链接到新链表的头(当前节点 2)。
    • 新链表:3 -> 2 -> 1
  • 依此类推,直到处理完所有节点,结果为:

  • 新链表:5 -> 4 -> 3 -> 2 -> 1

时间复杂度:O(n)

空间复杂度:O(n)

class ListNode:
    ##传入不同类型的数据时,将它们变为单链表
    def __init__(self, val):
        if isinstance(val, int):
            self.val = val
            self.next = None

        if isinstance(val, list):
            self.val = val[0]
            self.next = None
            head = self
            for i in range(1, len(val)):
                node = ListNode(val[i])
                head.next = node
                head = head.next

def ListReverse(head):
    new_head = None
    cur = head
    #可以理解为不断将当前节点推到新链表的头部,新链表当前节点的指向始终指向新链表头部
    while cur:
        new_node = ListNode(cur.val)
        new_node.next = new_head
        new_head = new_node
        cur = cur.next
    return new_head


if __name__ == '__main__':
    print('###################数组输入#########################')
    arr = [1,2,3,4,5]
    node = ListNode(arr)
    reversed_arr = ListReverse(node)
    while reversed_arr:
        #print(reversed_arr.val)
        reversed_arr = reversed_arr.next

    print('###################整数输入#########################')
    head = ListNode(1)
    p1 = ListNode(2)
    p2 = ListNode(3)
    p3 = ListNode(4)
    head.next = p1
    p1.next = p2
    p2.next = p3
    p = ListReverse(head)
    while p:
        print(p.val)
        p = p.next

过程描述: 在两个链表中布置初始指针,哪个value小就往下移动,当两个value相等时,打印这个key和对应value,再一起向后移动指针,继续之前的操作。直至有一个链表越界。

class ListNode:
    #构建双链表
    def __init__(self, val):
        if isinstance(val, int):
            self.val = val
            self.next = None
            self.prev = None

        if isinstance(val, list):
            self.val = val[0]
            self.next = None
            self.prev = None
            head = self
            for i in range(1,len(val)):
                node = ListNode(val[i])
                head.next = node
                node.prev = head
                head = head.next

def sameNum(head1, head2):
    cur1 = head1
    cur2 = head2
    while cur1 != None and cur2 != None:
        if cur1.val < cur2.val:
            cur1 = cur1.next

        elif cur2.val < cur1.val:
            cur2 = cur2.next

        else:
            print(cur1.val)
            cur2 = cur2.next
            cur1 = cur1.next


if __name__ == '__main__':
    p0 = ListNode(1)
    p1 = ListNode(2)
    p2 = ListNode(5)
    p3 = ListNode(6)
    p0.next = p1
    p1.next = p2
    p2.next = p3
    p1.prev = p0
    p2.prev = p1
    p3.prev = p2
    q0 = ListNode(2)
    q1 = ListNode(3)
    q2 = ListNode(4)
    q0.next = q1
    q1.next = q2

    sameNum(p0, q0)

方法1:使用栈来处理,从左往右放入栈中,存放完整链表后,开始弹出值并再从左往右与原链表比较,如果都相等就是回文。

方法2:只将链表右侧部分放入栈中,其他与方法1一样。这种方法需要用快慢指针方法来找中间点,快慢指针就是慢指针一次只移动1位,快指针一次移2位,当快指针走完链表时,慢指针就在链表中间那个点。快慢指针方法还需要考虑链表元素个数是奇数还是偶数。

class ListNode:
    # 构建双向链表
    def __init__(self, val):
        if isinstance(val, int):
            self.val = val
            self.next = None
            self.prev = None

        if isinstance(val, list):
            self.val = val[0]
            self.next = None
            self.prev = None
            head = self
            for i in range(1, len(val)):
                node = ListNode(val[i])
                head.next = node
                node.prev = head
                head = head.next


class Palindromic:
    def __init__(self, head):
        self.head = head

    def searchMid(self):
        cur = self.head
        slow = cur
        fast = cur
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow  # 返回中点

    def is_palindromic(self):
        mid = self.searchMid()

        # 使用栈来存储后半部分的元素
        stack = []
        cur = mid

        # 将后半部分的元素存入栈中
        while cur:
            stack.append(cur.val)
            cur = cur.next

        # 逐个比较栈中的元素和前半部分的元素
        cur = self.head
        while cur != mid:
            if cur.val != stack.pop():
                return False  # 如果不匹配,返回 False
            cur = cur.next

        return True  # 如果所有的元素都匹配,返回 True


if __name__ == '__main__':
    # 创建一个回文链表:1 -> 2 -> 3 -> 2 -> 1
    head1 = ListNode(1)
    head1.next = ListNode(2)
    head1.next.next = ListNode(3)
    head1.next.next.next = ListNode(2)
    head1.next.next.next.next = ListNode(1)

    # 创建一个非回文链表:1 -> 2 -> 3 -> 4
    head2 = ListNode(1)
    head2.next = ListNode(2)
    head2.next.next = ListNode(3)
    head2.next.next.next = ListNode(4)

    # 利用 Palindromic 类判断回文性
    palindromic1 = Palindromic(head1)
    print("链表1是回文吗?", palindromic1.is_palindromic())  # 应输出 True

    palindromic2 = Palindromic(head2)
    print("链表2是回文吗?", palindromic2.is_palindromic())  # 应输出 False

对于快慢指针对于不同情况的定位需要熟练掌握,比如对于偶数数量的元素,有时要得到的慢定位为中点的前一个值,有时候可能需要中点的的后一个值,这些是根据题目情况来判断的。

方法3:要使额外空间复杂度达到O(1),即不使用额外空间。 使用快慢指针找到中点,将右半部分的链表逆序,中点指向None,最左与最右分别放置初始指针,两两比较,如果都相等就是回文,直到有一个指针指向None停止。

class ListNode:
    def __init__(self, val):
        if isinstance(val, int):
            self.val = val
            self.next = None


        if isinstance(val, list):
            self.val = val[0]
            self.next = None
            cur = self
            for i in range(1, len(val)):
                node = ListNode(val[i])
                cur.next = node
                cur = cur.next

class Palindromic:
    def __init__(self, head):
        self.head = head

    def searchMid(self):
        cur = self.head
        slow = cur
        fast = cur
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow  # 返回中点

    def is_palindromic(self):
        mid = self.searchMid()
        cur = mid
        pre = None
        while cur:
            next = cur.next
            cur.next = pre
            pre = cur
            cur = next
        #while循环后pre在原链表的最右侧
        mid.next = None
        n1 = pre
        n2 = self.head
        while n1 != None and n2 != None:
            if n1.val != n2.val:
                return False
            n1 = n1.next
            n2 = n2.next

        cur = pre
        pre = None
        while cur:
            next = cur.next
            cur.next = pre
            pre = cur
            cur = next

        return True


if __name__ == '__main__':
    # 创建一个回文链表:1 -> 2 -> 3 -> 2 -> 1
    head1 = ListNode(1)
    head1.next = ListNode(2)
    head1.next.next = ListNode(3)
    head1.next.next.next = ListNode(2)
    head1.next.next.next.next = ListNode(1)

    # 创建一个非回文链表:1 -> 2 -> 3 -> 4
    head2 = ListNode(1)
    head2.next = ListNode(2)
    head2.next.next = ListNode(3)
    head2.next.next.next = ListNode(4)

    # 利用 Palindromic 类判断回文性
    palindromic1 = Palindromic(head1)
    print("链表1是回文吗?", palindromic1.is_palindromic())  # 应输出 True

    palindromic2 = Palindromic(head2)
    print("链表2是回文吗?", palindromic2.is_palindromic())  # 应输出 False

链表课后练习按照面试笔试两套标准来做,即一套coding简单不注重空间复杂度,一套要实现更低的空间复杂度,练习coding能力。

代码简易方法,用快排处理node型的数组

面试方法:

过程描述:设置六个变量,小于头、尾,大于头、尾,等于头、尾。遍历链表,将第一个复合条件的节点设为相应组的头节点,最新的复合条件的节点设为尾节点。遍历完链表后,将三组的头尾用指向相连。

class ListNode:

    def __init__(self, val):
        if isinstance(val, int):
            self.val = val
            self.next = None


        if isinstance(val, list):
            self.val = val[0]
            self.next = None
            cur = self
            for i in range(1, len(val)):
                node = ListNode(val[i])
                cur.next = node
                cur = cur.next

def groupSort(head, val):
    cur = head
    sh = None
    st = None
    rh = None
    rt = None
    hh = None
    ht = None
    while cur:
        next_node = cur.next
        cur.next = None
        if cur.val < val:
            if sh == None:
                sh = cur
                st = cur
            else:
                st.next = cur
                st = cur

        if cur.val == val:
            if rh == None:
                rh = cur
                rt = cur
            else:
                rt.next = cur
                rt = cur

        if cur.val > val:
            if hh == None:
                hh = cur
                ht = cur
            else:
                ht.next = cur
                ht = cur
        cur = next_node
    if st:
        st.next = rh
    else:
        sh = rh

    if rt:
        rt.next = hh

    result = sh if sh is not None else (rh if rh is not None else hh)
    return result

if __name__ == '__main__':
    head1 = ListNode(6)
    head1.next = ListNode(2)
    head1.next.next = ListNode(2)
    head1.next.next.next = ListNode(4)
    head1.next.next.next.next = ListNode(1)

    val = 4
    sortLinkHead = groupSort(head1, val)
    while sortLinkHead:
        print(sortLinkHead.val)
        sortLinkHead =sortLinkHead.next

使用哈希表处理方法:

过程描述:

将使用节点类型的哈希表,哈希表内key储存原节点,value储存复制原节点得到的新节点, 遍历原链表获得以上内容的哈希表。

遍历原链表,查找节点对应哈希表的value,即该节点对应的复制节点。再根据原节点的next指针指向的节点,将新节点的指针指向对应节点的新节点,将rand指针指向应该指向的新节点。

class Listnode:
    def __init__(self, val):
        if isinstance(val, int):
            self.val = val
            self.next = None
            self.rand = None

        if isinstance(val, list):
            self.val = val[0]
            self.next = None
            cur = self
            for i in range(1, len(val)):
                node = Listnode(val[i])
                cur.next = node
                cur = cur.next
def copy_rand(head):
    cur = head
    map = {}
    while cur:
        #哈希表内key储存原节点,value储存复制原节点得到的新节点。
        map[cur] = Listnode(cur.val)
        cur = cur.next

    cur = head
    while cur:
        map[cur].next = map[cur.next] if cur.next else None
        map[cur].rand = map[cur.rand] if cur.rand else None
        cur = cur.next

    return map[head]

head = Listnode(1)
p1 = Listnode(2)
p2 = Listnode(3)
p3 = Listnode(4)
head.next = p1
p1.next = p2
p2.next = p3
head.rand = p2
p1.rand = p3
p2.rand = p1
new_link = copy_rand(head)
while new_link:
    print(new_link.val)
    if new_link.rand:
        print(new_link.rand.val)
    else:
        print("rand is None")
    new_link = new_link.next

 不使用哈希表的方法:

将原链表节点称为原节点,原链表复制的新节点称为新节点。将原链表变为原节点的next节点为新节点,即1-1'-2-2'-3-3'。原节点的next为新节点,原节点的rand为目标节点,目标节点的next为目标节点的新节点,所以新节点的rand目标节点为原节点rand目标节点next。做完这些操作后再把原链表与新链表next方向还原。

class Listnode:
    def __init__(self, val):
        self.val = val
        self.next = None
        self.rand = None

def copy_rand(head):
    cur = head
    #将原链表变为原节点的next节点为新节点
    while cur:
        cur_next = cur.next
        cur_copy = Listnode(cur.val)
        cur.next = cur_copy
        cur.next.next = cur_next
        cur = cur_next

    #设置新节点rand
    cur = head
    cur_copy = None
    while cur:
        cur_next = cur.next.next
        cur_copy = cur.next
        cur_copy.rand = cur.rand.next if cur.rand else None
        cur = cur_next

    new_head = head.next

    #还原链表
    cur = head
    while cur:
        cur_next = cur.next.next
        cur_copy = cur.next
        cur.next = cur_next
        cur_copy.next = cur_copy.next.next if cur_copy.next else None
        cur = cur_next

    return new_head

head = Listnode(1)
p1 = Listnode(2)
p2 = Listnode(3)
p3 = Listnode(4)
head.next = p1
p1.next = p2
p2.next = p3
head.rand = p2
p1.rand = p3
p2.rand = p1

new_link = copy_rand(head)
while new_link:
    print(new_link.val)
    if new_link.rand:
        print(new_link.rand.val)
    else:
        print("rand is None")
    new_link = new_link.next

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值