数组与链表--python

数组与链表

一、数组

(一)概述

数组是一种线性结构,将元素存储在连续的内存空间当中,在数组中的元素会有一个位置,我们用索引去记录该位置,这就是一个基本数组的构成。

在python中,数组的概念被列表所替代,内部可以存储不同数据类型的元素,不过实际工作当中,一个列表基本只存储一种数据类型。

(二)一维数组

1、定义数组

一维数组是最简单也是最常用的一种,一般是对一组数据进行存储,每个数据都有他的索引,可以快速的访问该数字。例如:

# 初始化数组(因为python中没有数组概念,所以用列表代替)
list_1 = list(int)
list_2 = []
list_3 = [1, 2, 3, 4, 5]

以上就是一维数组的正常创建格式,是不是很简单方便。如果我们想要访问某个数组,就可以**数组名[下标]**直接得到目标值。注意:首个元素的索引为0,因为其本质上是内存地址的偏移量,而首个元素的内存地址偏移量为0,所以他的索引为0也是非常合理的。如果对地址计算非常感兴趣,可以自行搜索下内存地址计算公式,本篇不做过多赘述。

2、操作数组

数组属于可变容器类型,这意味着我们可以对数组内的元素做操作,一般来说,我们对数组做的操作无非与增删改查和遍历五个动作。

首先是最简单的遍历:

# 一般采用for或者while来实现对数组的遍历
list_1 = [1, 2, 3, 4, 5]

# 这就是一个最简单的遍历,会把所有的元素全部走一遍
for i in list_1:
    print(i)

while i < len(list_1):
    print(list_1[i])
    i += 1

利用循环可以把整个数组都遍历一遍,这样的话我们就能得到数组中的每个元素了,接下来就是给数组增加元素

'''
增:
append(要添加的元素)      将元素添加到列表的末尾, 一次只能添加1个元素.
extend(容器类型)          将元素添加到列表的末尾, 一次可以添加多个元素.
insert(索引, 元素)        在列表的指定位置(索引), 插入元素.
'''
# 1. 定义列表
list1 = [10, 20, 30]

# 2. 演示 append() 函数
list1.append(40)

# 3. 演示 extend() 函数
list1.extend(50)   # 这里传入的类型是可迭代(可遍历)的类型.
list1.extend([15, 25, 35, 45])

# 4. 演示 insert() 函数
list1.insert(1, 60)   
list1.insert(10, 90)   # 这个索引, 最大, 最小我们写谁都行.
list1.insert(-30, 100)   # 这个索引, 最大, 最小我们写谁都行.
list1.insert(2, 150)  

# 5. 打印列表
print(list1)

需要注意的是,append是一个一个加,extend是可以做到多个一起加的,而insert可以加到自己想加的索引后。说完了增加,接下来就是删除。

'''
删:
remove(元素)       根据元素(内容), 从列表中删除指定的元素, 只删除第一个匹配到的, 不存在就报错.
pop(索引)          根据索引, 从列表中删除指定的元素, 不存在就报错.
del                如果跟的是索引, 就删除该元素, 如果跟的是列表, 就删除该列表(内存中没有了)
clear              清空列表中所有的元素, 但是列表还在.
'''
# 1. 定义列表, 记录元素.
list1 = [1, 2, 3, 4, 5, 2]

# 2. 测试 remove() 函数
# list1.remove(20)   # 报错
list1.remove(2)    # 删除元素2

# 3. 测试 pop() 函数
list1.pop(3)    # 删除索引为3的元素
# list1.pop(30)    # 报错

# 4. 测试 del() 函数
del list1[3]
del list1[-3]
del list1   # 不仅删除元素, 还把list1从内存中删除

# 5. 测试 clear() 函数
list1.clear()

# 6. 打印集合
print(list1)

删除元素有多种方式,要注意使用del时,删除的数组是从内存中删除,就像从来没有存在一样,后续调用也会报错。当我们想确定删除的元素到底消失没有时,我们需要用到查询函数。

'''
查:
index(元素, 开始索引, 结束索引)       查找元素在列表中的位置, 不存在就: 报错.
count(元素)           			  统计元素在列表中的总次数.
元素 in 列表          				判断列表中是否包含该元素, 包含:True, 不包含: False
元素 not in 列表      				判断列表中是否不包含该元素, 不包含:True, 包含: False
'''
# 1. 定义列表
list1 = [10, 20, 30, 30, 30]

# 2. 演示index函数
print(list1.index(30))  # 2
# print(list1.index(50))  # 报错

# 3. 演示count(元素)
print(list1.count(30))  # 3

# 4. 演示 in 的用法.
print(30 in list1)  # True
print(50 in list1)  # False

# 5. 演示 not in 的用法, 有点像 双重否定表肯定
print(30 not in list1)  # False
print(50 not in list1)  # True

以上就是查询的几种方法,而我们在平时的编程过程中,难免会遇到需要修改数据的时刻,前文提到数组是一种可变的容器,那么修改数组的值就显得格外简单了。

'''
列表[索引] = 修改后的值          根据索引, 修改列表中对应元素的值, 索引不存在, 就 报错
sort()      排序的意思, 里边可以写参数,  reverse=False(默认的, 升序),  reverse=True(降序)
reverse()   反转元素.
'''
# 1. 定义列表, 记录元素.
list1 = [1, 2, 11, 5, 3]

# 2. 测试修改列表的元素值.
list1[2] = 20
# list1[20] = 20   # 报错

# 3. 测试对列表元素 反转.
list1.reverse()   # [3, 5, 20, 2, 1]

# 4. 测试对列表元素 排序.
list1.sort()  # 默认, 升序
list1.sort(reverse=False)  # 同上
list1.sort(reverse=True)  # 降序, 即: 先升序排列, 然后反转.

# 降序排列分解动作.
list1.sort()   # 升序
list1.reverse() # 反转

# 5. 打印列表
print(list1)

以上就是对数组的修改动作,几种基本的操作种类就在这里,当然还有很多对数组操作的动作和函数,这需要读者自己在实际运用中慢慢探索。

(二)二维数组

有一维就会有二维,二维数组通常会运用到矩阵方面,也是常用的一种类型。其实他就是数组的嵌套,一个数组对另一个数组进行嵌套,一个一维数组就是外面嵌套数组的一个元素。

# 定义一个二维数组
list1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

二维数组与一维数组的区别就在于索引,list1[外部索引] [内部索引],这样标识就可以取到目标元素,那么很多问题就可以解决了,比如:

# 定义一个二维数组
list1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 取第一个数组的第一个元素
list1[0][0] = 1

只要你了解了二维数组的索引定义,下面的操作就可以和一维数组类似了,所以本文不再过多赘述。

三维数组,其实也就是在二维数组上再去嵌套一层数组,实际意义不大。一般实际编程中,数组的维度最大维持在二维,很少能有三维数组的用武之地,所以基本了解到二维数组即可。

二、链表

(一)概述

链表是一种线性结构,将元素储存在非连续非顺序的存储结构中。链表由一系列的结点组成,每个节点包括两部分:数据域与指针域,数据域存储数据元素,指针域存储下一个结点的指针。

由于链表存储的特性,链表没有办法像数组一样快速访问某个元素,只能通过遍历来寻找元素位置,而且由于必须要存储指针,所以所用空间相较数组来说也会较高。不过他的元素可以散落在内存中的各个位置,所以比较灵活。还有更多的区别下文会单独来说。

(二)单向链表

单向链表是最基础的一种链表,是链表中最简单的形式,每个节点包括两个域,链接域和信息域。链接指向链表的下一个节点,而最后一个节点指向None

head -> item.1  next -> item.2 next -> .... -> item.n next -> None

head保存首地址,item存储数据,而next指向下一个节点地址。

那么问题来了,我们要怎么去定义一个完整的链表,并对其做一些动作呢?

1、定义节点

节点数据包括数据元素和指针

'''
单链表节点
item存放数据元素
next是下一个节点的标识,也就是指针
'''
class Node(object):
    def __init__(self, item):
        self.item = item
        self.next = None

此时我们就已经创建了一个链表的类

2、定义单向链表

链表需要首地址,有首地址后才能一步一步往后加元素

(1)定义头指针
# 定义单链表的首地址指针head
class SingleLinkList(object):
    def __init__(self):
        self._head = None

有了这两个类后就可以正常的创建链表了

例如:

if __name__ == '__main__':
    # 创建列表
    link_list = SingleLinkList()
    # 创建节点
    node1 = Node(1)
    node2 = Node(2)
    # 将结点加到链表中
    link_list._head = node1
    # 将第一个结点的next指针指向下一个结点
    node1.next = node2
    # 访问链表第一个数据
    print(link_list._head.item)
    # 访问链表第二个数据
    print(link_list._head.next.item)

这样就算是创建了一个列表,并且加入了一个元素,如果数据量很少,还可以接受,但是当数据量非常大时,会显得代码十分的臃肿,所以我们需要在链表中加入一些操作方法,来实现快捷的增删改查。

(2)增删改查等操作

一般链表中我们会去增加一些常用的操作方法,例如:

  • is_empty() 判断是否为空

  • lenth() 获取链表的长度

  • items() 获取链表数据迭代器

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

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

  • insert(position, item) 在任意位置添加元素

  • remove(item) 删除节点

  • find(item) 查找结点是否存在

实现代码如下:

class SingleLinkList(object):
    def __init__(self):
        self._head = None

    # 判断链表是否为空
    def is_empty(self):
        return self._head is None

    # 计算链表长度
    def lenth(self):
        # 开局指针指向head
        cur = self._head
        count = 0
        # 指到None说明到了尾部
        while cur is not None:
            count += 1
            # 指向下一个
            cur = cur.next
        return count

    # 遍历链表获取元素
    def items(self):
        # 开局指针指向head
        cur = self._head
        while cur is not None:
            # 返回生成器,可以理解为DIY的range
            yield cur.item
            # 指向下一个
            cur = cur.next

    # 向链表头部加元素
    def add(self, item):
        # 先创建要加的元素的结点
        node = Node(item)
        # 新元素结点指向原来头部结点
        node.next = self._head
        # 头部结点改成新结点
        self._head = node

    # 向链表尾部加元素
    def append(self, item):
        # 先创建要加的元素的结点
        node = Node(item)
        # 判断是否是空链表
        if self.is_empty():
            # 如果是空的,那头指针直接指向新结点
            self._head = node
        else:
            # 不是空指针的话,就找到尾部,把尾结点next指向新结点
            cur = self._head
            while cur.next is not None:
                cur = cur.next
            cur.next = node

    # 向指定位置插入元素
    def insert(self, index, item):
        # 指定位置在第一个元素之前,在头部插入
        if index <= 0:
            self.add(item)
        # 指定位置超过尾部,在尾部插入
        elif index > (self.lenth() - 1):
            self.append(item)
        # 剩下都是合法的
        else:
            node = Node(item)
            cur = self._head
            # 找到需要插入的位置
            for i in range(index - 1):
                cur = cur.next
            # 指向下一个元素
            node.next = cur.next
            # 之前这个位置的元素指向插入元素
            cur.next = node

    # 删除某个元素(元素)
    def delete_item(self, item):
        cur = self._head
        pre = None
        # 遍历到结束
        while cur is not None:
            # 找到指定元素后
            if cur.item == item:
                # 如果开头就是
                if not pre:
                    self._head = cur.next
                # 如果是后面的
                else:
                    pre.next = cur.next
                return True
            # 没找到的话就朝后继续遍历
            else:
                pre = cur
                cur = cur.next
    
    # 删除某个元素(索引)
    def delete_index(self, index):
        cur = self._head
        if index == 0:
            cur.next = cur.next.next
            return
        if index > self.lenth():
            print('超出范围')
            return
        for i in range(index):
            if cur.next is not None:
                cur = cur.next
        cur.next = cur.next.next
    
    # 寻找元素
    def find(self, item):
        # 遍历
        return item in self.items()

上面就一些常用的操作,可以针对具体使用场景稍加改造,而当我们写好这些后,我们后续的一些使用就可以直接利用里面的函数了,例如:

if __name__ == '__main__':
    # 创建列表
    link_list = SingleLinkList()
    
    # 创建节点
    node1 = Node(1)
    node2 = Node(2)
    
    # 将结点加到链表中
    link_list._head = node1
    
    # 将第一个结点的next指针指向下一个结点
    node1.next = node2
    
    # 向头部加数据
    link_list.add(0)
    
    # 向尾部加数据
    for i in range(3, 6):
        link_list.append(i)
        
    # 插入数据
    link_list.insert(5, 10)
    
    # 查看列表所有数据
    for i in link_list.items():
        print(i, end=' ')
    print()
    
    # 删除某个元素(元素)
    link_list.delete_item(3)
    for i in link_list.items():
        print(i, end=' ')
    print()
    
    # 删除某个元素(索引)
    link_list.delete_index(2)
    for i in link_list.items():
        print(i, end=' ')
    print()
    
    # 查看某个数据
    print(link_list.find(3))

'''
结果:
0 1 2 3 4 10 5 
0 1 2 4 10 5 
0 1 2 10 5 
False
'''

到这里,基础的单向链表我们就完成了

(三)、环形链表

所谓环形链表,顾名思义,就是链表的首尾相连,形成了一个环形的链表。其余部分与单向链表无太多变化。

head -> item.1  next -> item.2 next -> .... -> item.n next -> None
/\																||
||---<-----------------<-------------<----------------------<---\/

head保存首地址,item存储数据,而next指向下一个节点地址

1、定义结点
'''
环形链表节点
item存放数据元素
next是下一个节点的标识,也就是指针
'''
class Node(object):
    def __init__(self, item):
        self.item = item
        self.next = None
2、定义环形链表

因为与单向链表类似,所以定义头指针和增删改查动作放到一起,方便读者阅读。重点关注涉及到头指针和尾指针的动作,这也是环形和单向的主要区别。

class SingleCycleLinkList(object):
    def __init__(self):
        self._head = None

    # 判断链表是否为空
    def is_empty(self):
        return self._head is None

    # 计算链表长度
    def lenth(self):
        # 链表为空
        if self.is_empty():
            return 0
        # 开局指针指向head
        cur = self._head
        count = 1
        # 指到None说明到了尾部
        while cur.next != self._head:
            count += 1
            # 指向下一个
            cur = cur.next
        return count

    # 遍历链表获取元素
    def items(self):
        # 链表为空
        if self.is_empty():
            return 0
        # 开局指针指向head
        cur = self._head
        while cur.next != self._head:
            # 返回生成器,可以理解为DIY的range
            yield cur.item
            # 指向下一个
            cur = cur.next
        # 最后输出头指针
        yield cur.item

    # 向链表头部加元素
    def add(self, item):
        """ 头部添加结点"""
        node = Node(item)
        if self.is_empty():  # 为空
            self._head = node
            node.next = self._head
        else:
            # 添加结点指向head
            node.next = self._head
            cur = self._head
            # 移动结点,将末尾的结点指向node
            while cur.next != self._head:
                cur = cur.next
            cur.next = node
        # 修改 head 指向新结点
        self._head = node

    # 向链表尾部加元素
    def append(self, item):
        # 先创建要加的元素的结点
        node = Node(item)
        # 判断是否是空链表
        if self.is_empty():
            # 如果是空的,那头指针直接指向新结点
            self._head = node
            node.next = self._head
        else:
            # 不是空指针的话,就找到尾部,把尾结点next指向新结点
            cur = self._head
            while cur.next != self._head:
                cur = cur.next
            cur.next = node
            node.next = self._head

    # 向指定位置插入元素
    def insert(self, index, item):
        # 指定位置在第一个元素之前,在头部插入
        if index <= 0:
            self.add(item)
        # 指定位置超过尾部,在尾部插入
        elif index > (self.lenth() - 1):
            self.append(item)
        # 剩下都是合法的
        else:
            node = Node(item)
            cur = self._head
            # 找到需要插入的位置
            for i in range(index - 1):
                cur = cur.next
            # 指向下一个元素
            node.next = cur.next
            # 之前这个位置的元素指向插入元素
            cur.next = node

    # 删除某个元素(元素)
    def delete(self, item):
        if self.is_empty():
            return
        cur = self._head
        pre = Node
        # 第一个元素为需要删除的元素
        if cur.item == item:
            # 链表不止一个元素
            if cur.next != self._head:
                while cur.next != self._head:
                    cur = cur.next
                # 尾结点指向 头部结点的下一结点
                cur.next = self._head.next
                # 调整头部结点
                self._head = self._head.next
            else:
                # 只有一个元素
                self._head = None
        else:
            # 不是第一个元素
            pre = self._head
            while cur.next != self._head:
                if cur.item == item:
                    # 删除
                    pre.next = cur.next
                    return True
                else:

                    pre = cur  # 记录前一个指针
                    cur = cur.next  # 调整指针位置
        # 当删除元素在末尾
        if cur.item == item:
            pre.next = self._head
            return True

    # 寻找元素
    def find(self, item):
        # 遍历
        return item in self.items()

细心的读者可以发现,和单向链表相比,环形链表的多余动作就是将尾指针指向头指针。

if __name__ == '__main__':
    # 创建列表
    link_list = SingleCycleLinkList()
    # 判空
    print(link_list.is_empty())
    # 加入数据,组成一个环形链表
    node1 = Node(1)
    node2 = Node(2)
    link_list._head= node1
    node1.next = node2
    node2.next = link_list._head
    # 向头部加数据
    link_list.add(0)
    # 向尾部加数据
    for i in range(3, 6):
        link_list.append(i)
    # 插入数据
    link_list.insert(5, 10)
    # 查看列表所有数据
    for i in link_list.items():
        print(i, end=' ')
    print()
    # 删除某个元素(元素)
    link_list.delete(3)
    for i in link_list.items():
        print(i, end=' ')
    print()
    # 删除某个元素(索引)
    link_list.delete(2)
    for i in link_list.items():
        print(i, end=' ')
    print()
    # 查看某个数据
    print(link_list.find(3))
    

'''
结果:
True
0 3 4 5 10 
0 4 5 10 
0 4 5 10 
False
'''

(四)、双向链表

双向链表相较于单向列表更为复杂,他在存储一份数据的同时,会存储两个指针,分别指向前方和后方,让链表的表现更加灵活。

head <-> item.1 next prev <-> item.2 next prev <-> … <-> item.n next prev <-> None

head 保存首地址,item 存储数据,next 指向下一结点地址,prev 指向上一结点地址

1、定义结点
'''
双向列表
有一个元素两个指针
两个指针分别指向前和后
'''
class Node(object):
    def __init__(self, item):
        # item存放数据元素
        self.item = item
        # next 指向下一个节点的标识
        self.next = None
        # prev 指向上一结点
        self.prev = None
2、定义双向链表

双向链表相比于单向链表,就是加了一个向前的指针prev

# 双向链表
class BilateralLinkList(object):
	
    # 定义头指针
    def __init__(self):
        self._head = None

    # 判空
    def is_empty(self):
        return self._head is None
	
    # 算长度
    def lenth(self):
        # 初始指针指向head
        cur = self._head
        count = 0
        # 指针指向None 表示到达尾部
        while cur is not None:
            count += 1
            # 指针下移
            cur = cur.next
        return count

    # 遍历所有元素
    def items(self):
        # 获取head指针
        cur = self._head
        # 循环遍历
        while cur is not None:
            # 返回生成器
            yield cur.item
            # 指针下移
            cur = cur.next
	
    # 在链表头加元素
    def add(self, item):
        node = Node(item)
        if self.is_empty():
            # 头部结点指针修改为新结点
            self._head = node
        else:
            # 新结点指针指向原头部结点
            node.next = self._head
            # 原头部 prev 指向 新结点
            self._head.prev = node
            # head 指向新结点
            self._head = node

    # 在链表尾加元素
    def append(self, item):
        node = Node(item)
        if self.is_empty():  # 链表无元素
            # 头部结点指针修改为新结点
            self._head = node
        else:  # 链表有元素
            # 移动到尾部
            cur = self._head
            while cur.next is not None:
                cur = cur.next
            # 新结点上一级指针指向旧尾部
            node.prev = cur
            # 旧尾部指向新结点
            cur.next = node

    # 插入元素
    def insert(self, index, item):
        if index <= 0:
            self.add(item)
        elif index > self.lenth() - 1:
            self.append(item)
        else:
            node = Node(item)
            cur = self._head
            for i in range(index):
                cur = cur.next
            # 新结点的向下指针指向当前结点
            node.next = cur
            # 新结点的向上指针指向当前结点的上一结点
            node.prev = cur.prev
            # 当前上一结点的向下指针指向node
            cur.prev.next = node
            # 当前结点的向上指针指向新结点
            cur.prev = node
	
    # 删除结点
    def delete(self, item):
        if self.is_empty():
            return
        cur = self._head
        # 删除元素在第一个结点
        if cur.item == item:
            # 只有一个元素
            if cur.next is None:
                self._head = None
                return True
            else:
                # head 指向下一结点
                self._head = cur.next
                # 下一结点的向上指针指向None
                cur.next.prev = None
                return True
        # 移动指针查找元素
        while cur.next is not None:
            if cur.item == item:
                # 上一结点向下指针指向下一结点
                cur.prev.next = cur.next
                # 下一结点向上指针指向上一结点
                cur.next.prev = cur.prev
                return True
            cur = cur.next
        # 删除元素在最后一个
        if cur.item == item:
            # 上一结点向下指针指向None
            cur.prev.next = None
            return True

    # 查找元素
    def find(self, item):
        return item in self.items()

例如:

if __name__ == '__main__':
    # 创建列表
    link_list = BilateralLinkList()
    # 判空
    print(link_list.is_empty())
    # 加入数据,组成一个环形链表
    node1 = Node(1)
    node2 = Node(2)
    link_list._head= node1
    node1.next = node2
    # 向头部加数据
    link_list.add(0)
    # 向尾部加数据
    for i in range(3, 6):
        link_list.append(i)
    # 插入数据
    link_list.insert(5, 10)
    # 查看列表所有数据
    for i in link_list.items():
        print(i, end=' ')
    print()
    # 删除某个元素(元素)
    link_list.delete(3)
    for i in link_list.items():
        print(i, end=' ')
    print()
    # 查看某个数据
    print(link_list.find(3))
    
    
'''
True
0 1 2 3 4 10 5 
0 1 2 4 10 5 
False
'''
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值