• Python3数据结构与算法之线性表


    在此开始记录数据结构与算法(基于Python的)学习,主要参考视频https://www.bilibili.com/video/av21540971/?p=1

    线性表

    线性表有两种存储结构:顺序存储结构与链式存储结构。

    1 顺序表

      比如 int 类型的数据1,2,3,4要以顺序表格式存储,首先每个整型数据占4个字节(每个字节8位),计算机会分配4*4个连续的字节空间给它们,让它们依次存储。python中 list 可以放置不同类型的元素,比如 a = [1, 'adaf' , 2.6],不同元素每个所占内存又不同,它是怎么存储的尼?它其实是元素外置的顺序表。虽然每个元素所占的空间大小不同,可是储存元素的起始地址所占大小一样。比如1,占四个字节,它可能是从0x100(地址)开始存储;‘adafo’占五个字节,它可能从0x11开始存储。注意要存储地址编号,所需要的空间大小是一样的。所以 list 是顺序存储的 0x100,0x11...。其实python中已经分装好了,不需要考虑。

      其他语言中构建一个顺序表的完整信息应该包括两个部分:表头(告知应该分配多大空间,里面包含多少元素)eg, li[8],真实信息。但是Python是动态语言,其实不需要考虑表头。表头与数据区的存储又分为:一体式与分离式两种。分离式好

    2 链表

    2.1 单链表

      单链表就是元素+节点(节点指向下一个)

    class SingleNode(object):
        '''
        单链表的节点
        '''
        def __init__(self,item):
            # _item存放数据元素
            self.item = item
            # _next存放下一个节点的标识
            self.next = None

      单链表的操作:

      1)is_empty()  链表是否为空

      2)length()  链表长度

      3)travel()  遍历整个链表

      4) add(item) 链表头部添加元素

      5)append(item) 链表尾部添加元素

      6)insert(pos, item) 指定位置添加元素

      7)remove(item) 删除节点,注:相同元素时,删除第一个,查找也是一样

      8)search(item) 查找节点是否存在

    编程前先理解一下python中的等于,方便编程。

    为什么,a = 1, b =2 , a,b = b,a 可以。为什么python中变量不用声明,int a = 1。可以理解为引用。

    如图,a中存放的是一个地址,地址指向1这个数,同理b;然后箭头交叉就可以了。所以python中的等于有点像->

    #!/usr/bin/env python
    # -*-coding:utf-8 -*-
    
    class Node(object):
        # 节点
        def __init__(self, elem):
            # _item存放数据元素
            self.elem = elem
            # _next存放下一个节点的标识
            self.next = None
    
    class SingleLinkList(object):
        """单链表"""
    
        def __init__(self, node=None):
            self.__head = node
    
        def is_empty(self):
            """链表是否为空"""
            return self.__head==None
    
        def length(self):
            """链表长度"""
            # 定义cur游标,用来移动遍历节点
            cur = self.__head
            # count记录数量
            count = 0
            while cur != None:
                count = count + 1
                cur = cur.next
            # 退出循环时,cur指向的是尾节点的下一个(None)
            return count
    
        def travel(self):
            """遍历整个链表"""
            # 定义cur游标,用来移动遍历节点
            cur = self.__head
            while cur != None:
                print(cur.elem, end=" ")
                cur = cur.next
            print("")
    
    
    
        def add(self, item):
            """单链表头部添加元素"""
            # 对于任意一种插入,一般都是先动插入那个节点的指针,最后再打破原来的链表
            node = Node(item)
            node.next = self.__head
            self.__head = node
    
    
        def append(self, item):
            """链表尾部添加,尾插法"""
            node = Node(item)
            # 处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了
            if self.is_empty():
                self.__head = node
            else:
                cur = self.__head
                while cur.next != None:
                    cur = cur.next
                # 退出循环时,cur指向的是尾节点
                cur.next = node
    
    
        def insert(self, pos, item):
            '''
            指定位置添加,单项循环与单链表一样
            :param pos: 从0开始
            :param item:
            :return:
            '''
            # 考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插
            if pos<=0:
                self.add(item)
            elif pos > self.length()-1:
                self.append(item)
            else:
                pre = self.__head     # 这时游标指向的是第一个节点,不是head
                count = 0
                while count < (pos-1):
                    count = count+1
                    pre = pre.next
                # pre指向的是插入位置的前一个节点,比如插入位置是2,这时指向的是位置1的节点
                # 插入一般都是先设置插入节点的指针(游标),保证链表不断,然后再改变原有链表哪个节点的next
                node = Node(item)
                node.next = pre.next
                pre.next = node
    
    
        def remove(self,item):
            """删除指定元素的节点(多个相同删除第一个)"""
            cur = self.__head
            pre = None
            while cur != None:
                if cur.elem == item:
                    # 特殊情况,看是否是头节点
                    if cur == self.__head:
                        # 头结点的情况
                        self.__head = cur.next
                    else:
                        # 找到删除节点
                        pre.next = cur.next
                    return
                else:
                    # 同步移动两个游标,先移动前一个
                    pre = cur
                    cur = cur.next
    
    
        def search(self, item):
            """查找节点是否存在"""
            cur = self.__head
            while cur != None:
                if cur.elem == item:
                    return True
                else:
                    cur = cur.next
            return False
    
    if __name__ == '__main__':
        '''测试'''
        # 先创建单链表对象
        ll = SingleLinkList()
        print(ll.is_empty())
        print(ll.length())
    
        ll.append(1)   # 尾插
        ll.append(2)
        ll.append(3)
        ll.add(8)       # 头插
        ll.insert(1,0.5)  # 位置插
        ll.insert(0, 7.5)
        ll.insert(10, 4)
        ll.travel()       # 假如是print(ll.travel()),输出会多一个None
        ll.remove(7.5)     # 删除头
        ll.remove(2)
        ll.remove(4)
        ll.travel()
        print(ll.search(1))
    单链表

    2.2 单向循环链表

    与单链表的区别就在于它的尾节点指向链表的第一个元素。

    #!/usr/bin/env python
    # -*-coding:utf-8 -*-
    
    class Node(object):
        # 节点
        def __init__(self, elem):
            # _item存放数据元素
            self.elem = elem
            # _next存放下一个节点的标识
            self.next = None
    
    class SingleCycleLinkList(object):
        """单向循环链表"""
    
        def __init__(self, node=None):
            self.__head = node
            # 单项循环,有节点时得指向自己
            if node:
                node.next = node
    
        def is_empty(self):
            """链表是否为空"""
            return self.__head==None
    
        def length(self):
            """链表长度"""
            if self.is_empty():
                return 0
            # 定义cur游标,用来移动遍历节点
            cur = self.__head
            # count记录数量
            count = 1           # 必须从1开始,与单链表不同
            while cur.next != self.__head:
                count = count + 1
                cur = cur.next
            return count
    
        def travel(self):
            """遍历整个链表"""
            if self.is_empty():
                return
            # 定义cur游标,用来移动遍历节点
            cur = self.__head
            while cur.next != self.__head:
                print(cur.elem, end=" ")
                cur = cur.next
            # 退出循环,cur指向尾节点,但是尾节点的元素没有打印
            print(cur.elem)
    
    
        def add(self, item):
            """单向循环链表头部添加元素"""
            node = Node(item)
            if self.is_empty():
                self.__head = node
                node.next = node
            else:
                cur = self.__head
                while cur.next != self.__head:
                    cur = cur.next
                # 退出循环,cur指向尾节点
                node.next = self.__head
                self.__head = node
                # cur.next = node   # 与下面那行等价
                cur.next = self.__head
    
    
        def append(self, item):
            """链表尾部添加,尾插法"""
            node = Node(item)
            # 处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了
            if self.is_empty():
                self.__head = node
                node.next = node
            else:
                cur = self.__head
                while cur.next != self.__head:
                    cur = cur.next
                cur.next = node
                node.next = self.__head
    
        def insert(self, pos, item):
            '''
            指定位置添加,单项循环与单链表一样
            :param pos: 从0开始
            :param item:
            :return:
            '''
            # 考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插
            if pos<=0:
                self.add(item)
            elif pos > self.length()-1:
                self.append(item)
            else:
                pre = self.__head     # 这时游标指向的是第一个节点,不是head
                count = 0
                while count < (pos-1):
                    count = count+1
                    pre = pre.next
                # pre指向的是插入位置的前一个节点,比如插入位置是2,这时指向的是位置1的节点
                # 插入一般都是先设置插入节点的指针(游标),保证链表不断,然后再改变原有链表哪个节点的next
                node = Node(item)
                node.next = pre.next
                pre.next = node
    
    
        def remove(self,item):
            """删除指定元素的节点(多个相同删除第一个)"""
            if self.is_empty():
                return
            cur = self.__head
            pre = None
            while cur.next != self.__head:
                if cur.elem == item:
                    # 特殊情况,看是否是头节点
                    if cur == self.__head:
                        # 头结点的情况
                        rear_cur = self.__head
                        while rear_cur.next != self.__head:
                            rear_cur = rear_cur.next
                        self.__head = cur.next
                        # 退出循环时,rear_cur指向的是尾节点
                        rear_cur.next = self.__head
                    else:
                        # 找到删除节点
                        pre.next = cur.next
                    return
                else:
                    # 同步移动两个游标,先移动前一个
                    pre = cur
                    cur = cur.next
    
            # 退出循环,cur指向尾节点,需要判断一下当前的item
            if cur.elem == item:
                if cur == self.__head:
                    # 链表只有一个节点,此时pre为None
                    self.__head = None
                else:
                    pre.next = cur.next
    
        def search(self, item):
            """查找节点是否存在"""
            cur = self.__head
            while cur.next != self.__head:
                if cur.elem == item:
                    return True
                else:
                    cur = cur.next
            # 退出循环cur指向尾节点
            if cur.elem == item:
                return True
            return False
    
    if __name__ == '__main__':
        '''测试'''
        # 先创建单链表对象
        ll = SingleCycleLinkList()
        print(ll.is_empty())
        print(ll.length())
    
        ll.append(1)   # 尾插
        ll.append(2)
        ll.append(3)
        ll.add(8)       # 头插
        ll.insert(1,0.5)  # 位置插
        ll.insert(0, 7.5)
        ll.insert(10, 4)
        ll.travel()       # 假如是print(ll.travel()),输出会多一个None
        ll.remove(7.5)     # 删除头
        ll.remove(2)
        ll.remove(4)
        ll.travel()
        print(ll.search(1))
    单向循环链表

    2.3 双向链表

    “双向链表”又称为“双面链表”,每一个节点有两个链接:一个指向前一个,一个指向后一个。对于首节点,指向前一个的指空,同理尾节点。

    #!/usr/bin/env python
    # -*-coding:utf-8 -*-
    
    class Node(object):
        # 双向节点
        def __init__(self, elem):
            self.elem = elem
            self.prev = None     # 前驱
            self.next = None     # 后驱
    
    class DoubleLinkList(object):
        """双链表,其__init__,is_empty,length,travel,search,单链表一样,故可以利用多态的概念,直接传入单链表代替object"""
    
        def __init__(self, node=None):
            self.__head = node
    
        def is_empty(self):
            """链表是否为空"""
            return self.__head is None
    
        def length(self):
            """链表长度"""
            # 定义cur游标,用来移动遍历节点
            cur = self.__head
            count = 0
            while cur != None:
                count = count + 1
                cur = cur.next
            # 退出循环时,cur指向的是尾节点的下一个(None)
            return count
    
        def travel(self):
            """遍历整个链表"""
            # 定义cur游标,用来移动遍历节点
            cur = self.__head
            while cur != None:
                print(cur.elem, end=" ")
                cur = cur.next
            print("")
    
    
    
        def add(self, item):
            """双链表头部添加元素"""
            # 对于任意一种插入,一般都是先动插入那个节点的指针,最后再打破原来的链表
            node = Node(item)
            if self.is_empty():
                self.__head = node
            else:
                # 插入节点的后驱指向原首节点
                node.next = self.__head
                # 原首节点的前驱指向插入节点
                self.__head.prev = node
                # 头指向新的首节点
                self.__head = node
    
    
        def append(self, item):
            """链表尾部添加,尾插法"""
            node = Node(item)
            # 处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了
            if self.is_empty():
                self.__head = node
            else:
                cur = self.__head
                while cur.next != None:
                    cur = cur.next
                # 退出循环时,cur指向的是尾节点
                cur.next = node
                node.prev = cur
    
    
        def insert(self, pos, item):
            '''
            指定位置添加,单项循环与单链表一样
            :param pos: 从0开始
            :param item:
            :return:
            '''
            # 考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插
            if pos<=0:
                self.add(item)
            elif pos > self.length()-1:
                self.append(item)
            else:
                cur = self.__head
                count = 0
                while count < pos:
                    count = count+1
                    cur = cur.next
                # 退出循环时cur指向pos位置
                node = Node(item)
                node.next = cur
                node.prev = cur.prev
                cur.prev.next = node
                cur.prev = node
    
    
        def remove(self,item):
            """删除指定元素的节点(多个相同删除第一个)"""
            cur = self.__head
            while cur != None:
                if cur.elem == item:
                    # 特殊情况,看是否是头节点
                    if cur == self.__head:
                        # 头结点的情况
                        self.__head = cur.next
                        if cur.next:             # else的情况是只有一个节点,cur.next无prev
                            cur.next.prev = Node
                    else:
                        # 找到删除节点,cur指向要删除的节点
                        cur.prev.next = cur.next
                        if cur.next:
                            # 尾节点
                            cur.next.prev = cur.prev
                    return
                else:
                    cur = cur.next
    
    
        def search(self, item):
            """查找节点是否存在"""
            cur = self.__head
            while cur != None:
                if cur.elem == item:
                    return True
                else:
                    cur = cur.next
            return False
    
    if __name__ == '__main__':
        '''测试'''
        # 先创建单链表对象
        ll = DoubleLinkList()
        print(ll.is_empty())
        print(ll.length())
    
        ll.add(100)
    
        ll.append(1)   # 尾插
        ll.append(2)
        ll.append(3)
        ll.add(8)       # 头插
        ll.insert(1,0.5)  # 位置插
        ll.insert(0, 7.5)
        ll.insert(10, 4)
        ll.travel()       # 假如是print(ll.travel()),输出会多一个None
        ll.remove(7.5)     # 删除头
        ll.remove(2)
        ll.remove(4)
        ll.travel()
        print(ll.search(1))
    双链表

     3 有关链表的题目

    3.1 单链表反转

  • 相关阅读:
    加密配置节
    配置使用 SQL Server提供程序 。
    文件上传
    未能正确加载包"visla Studio HTM Editor Package" 问题的解决
    AspNetPager分页控件的使用
    配置MIME一览
    摸器械的结果。。。
    爱一个人
    这两天怪哉。。。
    “落花有意随流水,流水无情恋落花。”出处
  • 原文地址:https://www.cnblogs.com/maxiaonong/p/10445295.html
Copyright © 2020-2023  润新知