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(地址)开始存储;‘adaf\o’占五个字节,它可能从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 单链表反转

 

转载于:https://www.cnblogs.com/maxiaonong/p/10445295.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值