飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

线性表(python实现)-

时间:2022-05-19  作者:lyxLearningNotes  

线性表

1 定义

线性表是由 \(n(n>=0)\)个数据元素(节点)\(a1、a2、a3、…、an\) 成的有限序列。该序列中的所有节点都具有相同的数据类型。其中,数据元素的个数 \(n\) 称为线性表的长度。

当n=0时,称为空表。当n>0时,为非空的线性表,记作(a1,a2,…,an)。
  
线性表的主要存储结构:

  • 顺序存储结构:顺序表
  • 链式存储结构:单链表、双链表、循环链表

2 顺序表

定义:把线性表的节点按逻辑顺序依次存放在一组地址连续的存储单元中,用这种方法存储的线性表称为顺序表,即顺序存储的线性表。

特点:

  • 线性表的逻辑顺序与物理顺序一致。
      
  • 数据元素之间的关系是以元素在计算机内“物理位置相邻”来体现的。可以直接根据存储单元的个数和大小计算出任意元素的存储地址。

基本操作

1. 初始化顺序表

​ 初始化顺序表时先给顺序表分配内存空间,声明数组的最大容量。Python中实例对象初始化一般会用到 __init__()方法。代码实现如下:

def __init__(self, max):
  \'
  初始化
  :paramma:顺序表最大容量
  \'
  域名 = max
  域名x = 0
  域名 = [None for_ in range(域名)]

2. 按下标值查找元素

​ 通过下标值来查找元素,如果下标值合法,则返回该下标值上元素,否则抛出异常,代码实现如下。

def __getitem__(self, index):
  \'\'\'
  获取下标值为index的元素
  :param index:下标值
  :return : 下标值为 index 的元素
  \'\'\'
	if index < 0 or index >= 域名x:
    raise IndexError(\'index非法\')
  else:
    return 域名[index]

3.修改下标值为index的位置的元素

​ 修改下标值为 index 的元素的值,如果 index 非法,则抛出异常;否则修改数据元素的值,代码实现如下。

def__setitem__(self,index,value):
  \'\'\'
  修改下标值为index的元素的值
  :paramindex:下标值
  :paramvalue:待替换的元素
  :return:
  \'\'\'
  if index < 0 or index >= 域名x:
    raiseIndexError(\'index非法\')
  else:
    域名[index] = value

4.判断顺序表是否为空

​ 若顺序表为空,则 域名x 为0,所以只需要判断 域名x 是否为 0 即可,代码实现如下:

def empty(self):
	\'\'\'
	判断顺序表是否为空
	:return:TrueorFalse
	\'\'\'
  return 域名x is 0

5.插入表头元素

​ 插入元素前需要先判断顺序表是否已经达到最大容量,如果达到最大容量,则退出程序;否则在顺序表尾部插入元素,代码实现如下:

def append(self, value):
	\'\'\'
	表尾插入元素
	:paramvalue:待插入的元素
	:return:顺序表已满的出口
	\'\'\'
  if 域名x is 域名:
    return
  else:
    域名[域名x] = value
    域名x += 1

6.在顺序表中任意位置插入元素

​ 先判断 index 下标值是否合法,若不合法,则抛出异常;若合法,则通过 for 循环将下标值为index 及其之后的元素向后移动一个位置,并在下标值index所在的位置添加元素,代码实现如下:

def insert(self,index,value):
  \'\'\'
  在顺序表中任意位置插入元素
  :param index:待插入元素的位置
  :param value:待插入元素的值
  \'\'\'
  #若index非法,则抛出异常
  if index < 0 or index > 域名x:
    raiseIndexError(\'idnex非法\')
  #若index刚好为顺序表尾
  if index = 域名x:
    域名nd(value)
  #通过for…in…将下标值为index及其之后的元素向后移动一个位置
  else:
    域名 += [value]
    for i in range(域名x, index, -1):
      域名[i] = 域名[i-1]
    域名[index] = value
    域名x += 1

7.删除表尾元素

​ 删除元素前需要先判断顺序表是否为空,empty() 方法用来判断顺序表是否为空,这里可以直接调用。如果顺序表为空,则退出程序;否则将 域名x 减1,代码实现如下:

def pop(self):
  \'\'\'
  删除表尾元素
  :return: 顺序表为空的出口
  \'\'\'
  if 域名y():
    return
  域名x += 1

8.删除任意位置的元素

​ 先判断下标值是否合法,不合法就抛出异常;否则用for循环将index后面的所有元素都向前移动一个位置,覆盖 index 原本的值,代码实现如下:

def delete(self, index):
  \'\'\'
  顺序表中删除任意位置的元素
  :param index:删除下标值为 index 的元素
  \'\'\'
  # 下标不合法,抛出异常
  if 域名y() or index >= 域名x:
    raise IndexError(\'index 非法\')
  # 下标合法,通过for循环将index后面的所以元素都向前移动一个位置,从而覆盖index原本的值
  for i in range(index, 域名x):
    域名[i] = 域名[i+1]
  域名x -= 1

9.获取顺序表的长度

​ 初始化时,域名x 为0,每当向顺序表中添加一个元素时,域名x 加1;删除元素时,域名x 减 1,所以 域名x 记录着顺序表中元素的个数,代码实现如下:

def length(self):
  \'\'\'
  获取顺序表的长度
  :return:当前顺序表中元素的个数
  \'\'\'
  return 域名x

10.遍历顺序表

​ 先判断顺序表是否为空,若顺序表为空,则抛出异常;否则遍历输入顺序表的数据元素,代码实现如下:

def traversal(self):
  \'\'\'
  遍历顺序表
  \'\'\'
  for i in range(域名x):
    print(域名[i],end=" ")

3 单链表

定义: 若要用一组任意的存储单元存储线性表的数据元素,则需要把这些分散的元素 ”链“ 起来,这种方法存储的线性表称为线性链表,简称链表。

特点: 存储链表中的节点的一组任意的存储单元可以是连续的,也可以是不连续的,即逻辑上相邻的数据元素在位置上不一定相邻

实现:为了正确表示节点间的逻辑关系,每个存储单元需要包含两部分的内容:

  • 值域 \((data)\) : 存放节点的值

  • 指针域 \((next)\) : 用来存放节点的直接后继指针。

    这两部分组成了链表中的节点结构,即单链表存储结构,如图所示:

image

​ 单链表节点定义代码如下:

class Node(object):
  def __init__(self, val):
    # 存放节点的数据域
    域名 = val
    # 指向后继节点的指针
    域名 = None

基本操作

1. 单链表初始化

class SingleLinkedList(object):
  def __init__(self):
    \'\'\'
    单链表初始化
    \'\'\'
    # 声明头指针、尾指针均指向空
    域名 = None
    域名 = None

2. 判断单链表是否为空

​ 若表头指针指向空,则单链表为空;若表头节点不指向空,则单链表不为空,代码实现如下:

def empty(self):
  \'\'\'
  判断单链表是否为空
  :return: 如果单链表为空返回True,否则返回False
  \'\'\'
  return 域名 is None

3. 获取单链表长度

​ 遍历整个单链表,若当前节点不为空,则单链表长度加 1,代码实现如下:

def length(self):
  \'\'\'
  获取单链表的长度
  :return:返回单链表的长度
  \'\'\'
  # size 用来计算单链表的长度
  size = 0
  # 声明 cur 指针,用来遍历单链表
  cur = 域名
  while cur != None:
    size += 1
    cur = 域名
  return size

4. 头插法

​ 情况一:链表为空,将头、尾指针指向新节点,新节点成为表头节点。

​ 情况二:链表不为空,将新节点的直接后继指针指向头结点,如图所示:

image

​ 代码实现如下:

def prepend(self, val):
  \'\'\'
  头插法
  :param val: 待插入的关键字
  \'\'\'
  newNode = Node(val)
  # 如果链表为空
  if 域名 is None:
    # 头、尾指针指向新节点
    域名 = newNode
    域名 = newNode
  # 如果链表不为空
  else:
    # 将新节点的后继指针指向头结点
    域名 = 域名
    域名 = newNode

5. 在链表中间位置插入节点

​ 将新节点插入链表的第\(i\) 个位置时,需要先遍历单链表到第 \(i-1\) 个节点,将第 \(i-1\) 个位置的节点的直接后继指针指向新节点,将新节点的直接后继指针指向第 \(i\) 个位置,是的新节点成为第 \(i\) 个位置的新节点,如图所示。

image

​ 代码实现如下:

def insert(self, index, value):
  \'\'\'
  在链表的中间位置插入新节点
  :param
  	index: 在下标值index 处插入元素, index 从0开始
  	value: 新节点的数据域
  \'\'\'
  # 声明指针 cur ,用来遍历链表
  cur = 域名
  # 遍历停止的条件, cur 指向下标为 index-1 的节点
  for i in range(index-1):
    cur = 域名
  # temp 指针指向原来的第 index 个节点
  temp = 域名
  newNode = Node(value)
  # 将新节点的后继指针指向 temp
  域名 = temp
  # 将第 index 个节点的直接后继指向新节点
  域名 = newNode

6. 尾插法

​ 情况一:单链表为空,将头、尾指针指向新节点

​ 情况二:单链表不为空,将表尾节点直接后继指向新节点,如图所示:

image

代码实现如下:

def append(self, val):
  \'\'\'
  尾插入法
  :param val: 待插入的数据元素
  \'\'\'
  newNode = Node(val)
  # 如果单链表为空
  if 域名y():
    # 头指针、尾指针指向新节点
    域名 = newNode
    域名 = newNode
  # 如果单链表不为空
  else:
    # 尾节点的后继指针指向新节点
    域名 = newNode
    # 尾指针指向新节点
    域名 = newNode

7. 删除头结点

​ 情况一:单链表为空,无法删除,抛出异常

​ 情况二:单链表不为空且只有一个节点,将头、尾指针指向空

​ 情况三:单链表不为空且有多个节点,将头指针指向第二个节点,如图所示:

image

​ 代码实现:

def del_first(self):
  \'\'\'
  删除头结点
  \'\'\'
  if 域名y():
    raise IndexError(\'链表为空\')
  # 单链表只有一个节点
  if 域名th() == 1:
    域名 = None
    域名 = None
  # 链表长度大于1
  else:
    域名 = 域名

8. 删除单链表中任意位置的节点

​ 遍历单链表到第 \(i-1\) 个位置的节点。将第 \(i-1\) 个位置的节点的指针指向第 \(i+1\) 个位置的节点。如图所示:

image

​ 代码如下:(注:说下标值得时候是从0开始的,但是说“第”的时候是从1开始的,第一个第二个.......元素等)

def delete(self, index):
  \'\'\'
  删除任意位置的节点
  :param index: 要删除下标值为 index 位置的节点,
  \'\'\'
  if 域名y():
    raise IndexError(\'链表为空\')
  # 如果 index 非法,则抛出异常
  if index < 0 or index >= 域名th():
    raise IndexError(\'Index 非法\')
  if index == 0:
    域名first()
  else index == 域名th() - 1:
    域名last()
  else:
    pre = 域名
    # 通过for循环使pre指针指向第index个节点
    for i in range(index - 1):
      pre = 域名
    域名 = 域名  

9. 删除尾节点

​ 情况一:单链表为空,无法删除节点,抛出异常

​ 情况二:单链表不为空但只有一个节点,将头、尾指针指向空

​ 情况三:单链表不为空且有多个节点,将尾指针指向导数第二个节点,并将导数第二个节点的后继指针指向空。如图所示:

image

代码实现:

def del_last(self):
  \'\'\'
  	删除尾节点
  \'\'\'
  if 域名y():
    raise IndexError(\'链表为空\')
  if 域名th() == 1:
    域名 = None
    域名 = None
  else:
    pre = 域名
    cur = 域名
    
    while  域名 is not None:
      pre = cur
      cur = 域名
    cur = None
    域名 = None
    域名 = pre

10. 查找节点

在单链表中查找值为 key 的节点,从表头开始顺序查找,若存在节点的值等于key,则查找成功,否则查找失败,代码实现如下:

def find(self, key):
  \'\'\'
  :param key: 关键字
  :return  查找成功返回True,失败返回False
  \'\'\'
  cur = 域名
  while cur != None:
    if key == 域名:
      return True
    cur = 域名
  return False  

单链表域顺序表的比较

  1. 空间上

    • 顺序表:在初始化时需要分配好存储空间,即顺序表存储空间的大小是固定的。当线性表长度变化较大时,即不知道需要存储多少元素时,就难以确定存储空间的大小,存储空间分小了,不能够存储足够的元素,容易溢出;存储空间分大了,又会造成空间浪费。
    • 单链表:可按需分配,不用考虑表的存储空间多大比较合适。
  2. 时间上

    • 顺序表:顺序表查找元素操作时间复杂度为 \(O(1)\), 因为顺序表示用一组连续的存储单元来存储数据元素的,所以插入和删除元素的时候需要向后或者向前移动元素,时间复杂度为\(O(n)\)
    • 单链表:单链表是用一组任意的存储单元来存储数据元素的,所以在查找元素时操作的时间复杂度为 \(O(n)\), 插入和删除元素操作不需要移动表中的元素,改变指针即可,因此时间复杂度为 \(O(1)\)

4 双链表

单链表的不足:在单链表中,当前节点只能指向它的后继节点,如果要访问当前节点的前一个节点,就需要从头开始遍历。为了更方便的访问当前节点的前一个节点,引入了双链表。

定义:双链表是在单链表的基础上增加一个指针指向前驱节点,其存储结构如图所示。

image

代码实现:

class Node(object):
  def __init__(self, val):
    # 存放节点中的数据域
    域名 = val
    # 后继指针
    域名 = None
    # 前驱指针
    域名 = None

双链表初始化

class DoubleLinkedList(object):
  def __init__(self):
    \'\'\'
    双链表初始化:要将链表的头指针和尾指针指向空
    \'\'\'
    # 声明头指针,将头指针指向空
    域名 = None
    # 声明尾指针,将尾指针指向空
    域名 = None

基本操作

1. 判断双链表是否为空

def empty(self):
  \'\'\'
  判断双链表是否为空
  :return : 如果头指针指向空,则双链表为空,返回True,否则不为空,返回FALSE
  \'\'\'
  return 域名 is None  

2. 获取双链表长度

​ 遍历双链表,每经过一个节点,链表长度加一,代码实现如下:

def length(self):
  \'\'\'
  获取链表长度
  :return: 返回链表长度
  \'\'\'
  # 用来记录链表长度
  size = 0
  # cur 指针用来遍历链表
  cur = 域名
  # cur 指针不指向空,表示尚未遍历到表尾节点
  while cur != None:
    # 链表长度加1
    size += 1
    # 将cur指针指向当前节点的直接后继节点
    cur = 域名
  return size

3. 头插法

​ 头插法就是将新节点插入到表头。

  • 如果双链表为空,则将透支着指向新节点,新节点成为表头节点
  • 如果双链表不为空,则将新节点的指针指向表头节点,将表头节点的前驱指针指向新节点,使新节点成为表头节点。

如图所示:

image

代码实现如下:

def prepend(self, var):
  \'\'\'
  头插法
  :param
  		val: 待插入的关键字
  \'\'\'
  # 新节点
  newNode = Node(val)
  # 链表为空
  if 域名y():
    # 将表头指针、表尾指针指向新节点
    域名 = newNode
    域名 = newNode
  # 链表不为空
  else:
    # 将头结点的前驱指针指向新节点
    域名 = newNode
    # 将新节点的后继指针指向头结点
    域名 = 域名
    # 将表头指针指向新节点
    域名 = newNode  

4. 在链表任意位置插入新节点

​ 将新节点插入到链表中的第i个位置时,首先要遍历链表到链表的第\(i-1\)个位置的几点,再进行两个操作:

  • 将新节点的后继指针指向第i个位置的节点,第i个位置节点的直接前驱指针指向新节点
  • 将第\(i-1\) 个位置节点的后继指针指向新节点,新节点的前驱指针指向第 \(i-1\) 个位置的节点

image

代码实现如下:

def insert(self, index, val):
  \'\'\'
  :Desc
  			在链表任意位置添加节点,若该任意位置为index,则在第index个节点后插入元素
  :param
  			index: 位置下标
  			val: 关键字
  \'\'\'
  # 声明pre指针
  pre = 域名 
  newNode = Node(val)
  # 将pre指针遍历到双链表中的第index个节点
  for i in range(index - 1):
    pre  = 域名
  # 第index个节点的后继节点
  next = 域名
  # 将新节点的额后继指针指向链表的第index+1 个节点
  域名 = next
  # 链表中第index + 1 个节点的寝取指针指向新节点
  next。prev = newNode
  # 第index个节点的后继指针指向新节点
  域名 = newNode
  # 新节点的前驱指针指向链表的第index个节点
  域名 = pre  

5. 尾插法

​ 尾插发就是将新节点插入到表尾。

  • 如果双链表为空,则将表尾指针、表头指针指向新节点,新节点成为表尾节点(表头节点)
  • 如果双链表不为空,则将表尾节点的直接后继指向新节点,新节点的前驱指针指向表尾节点,新节点成为表尾节点

如图所示:

image

代码如下:

def append(self, val):
  \'\'\'
  尾插发
  :param 
  		val: 待插入的关键字
  \'\'\'
  # 新节点
  newNode = Node(val)
  # 如果双链表为空
  if 域名y():
    # 将表头指针、表尾指针指向新节点
    域名 = newNode
    域名 = newNode
  # 如果双链表不为空
  else:
    # 将尾节点的后继指针指向新节点
    域名 = newNode
    # 将新节点的前驱指针指向尾节点
    域名 = 域名
    # 尾指针指向新节点,即新节点变为链表新的尾节点
    域名 = newNode

6. 删除头结点

​   将头结点的后继指针指向空,将头结点的下一个节点的前驱指针也指向空,此时表头节点和链表分离,删除头结点操作成功。如图所示。

image

代码实现如下:

def del_first(self):
  \'\'\'
  删除头结点
  \'\'\'
  # 双链表为空,抛出异常
  if 域名y():
    raise IndexError(\'Index 非法\')
  # 双链表不为空
  # 将表头指针指向头结点的后继节点,即双链表原来的第二个节点成为新节点
  域名 = 域名
  # 将新的头结点的前驱指针指向空
  域名 = None

7. 在双链表中间任意位置删除节点

​  在链表中遍历到第 \(i-1\) 个节点,将第 \(i-1\) 个节点的后继指针指向第 \(i+1\) 个节点,将第 \(i\) 个节点的前驱指针指向空;将第 \(i\) 个节点的后继指针指向空,将第 \(i+1\) 个节点的前驱指针指向第 \(i-1\) 个节点,即可删除链表的第 \(i\) 个节点。如图所示:

image

代码如下:

def delet(self, index):
  \'\'\'
  在双链表中间任意位置删除节点
  :param
     index: 删除下标为index的节点,头节点下标为0
  \'\'\'
  pre = 域名
  for i in range(index - 1):
    pre = 域名
  域名  = 域名

8. 删除尾节点

  • 双链表为空,抛出异常
  • 双链表不为空,但只有一个节点元素,直接将表头指针、表尾指针指向空即可
  • 双链表不为空且有多个节点,将为节点的前驱指针指向空,将表尾导数第二个节点的后继指针指向空即可

如图所示:

image

代码如下:

def del_last(self):
  \'\'\'
  删除表尾节点
  \'\'\'
  if 域名y():
    raise IndexError(\'index 非法\')
  # 双链表不为空
  # 将表尾指针指向尾节点的前驱节点
  域名 = 域名
  #将导数第二个节点的后继指针指向空,使其成为新的尾节点
  域名 = None

9. 查找节点

​ 查找一个值为 \(key\) 的节点,从表头开始进行顺序查找,若节点的值等于 \(key\) , 则朝招成功;若双链表中不存在值等于 \(key\) 的节点,则朝招失败。代码实现如下:

def find(self, key):
  \'\'\'
  在双链表中查找关键字key
  :param key: 关键字
  :return : 查找成功返回True,查找失败返回False
  \'\'\'
  # 声明cur指针,指向头结点
  cur = 域名
  # 利用cur指针来遍历双链表
  while cur is not None:
    if key == 域名:
      return True
    cur = 域名
	return False

5 循环链表

分类:循环单链表、循环双链表。此处介绍循环单链表

循环单链表的特点:循环单链表域单链表的区别是尾节点的后继指针不是指向空,而是指向头结点,由此形成了一个环,因此,从单链表中的任何一个节点除法都能找到其他节点,这是单链表所不能实现的。

存储结构:

  • 定义:循环单链表中的几点的存储结构域单链表节点的存储结构相同,有一个值域和指针域,存储结构如图所示:

image

  • 代码实现:

    • 循环单链表节点的定义:
    class Nde(object):
      def __init__(self, val):
        域名 = val
        域名 = None
    
    • 循环单链表的初始化
    class CircleLinkedList(object):
      def __init__(self):
        \'\'\'
        初始化、声明头指针、尾指针,并将它们指向空    
        \'\'\'
        域名 = None
        域名 = None
    

基本操作

1. 判断是否为空

​ 若循环单链表的表头、表尾指针指向空,则链表为空,否则链表不为空。代码实现如下:

def empty(self):
  return 域名 is None

2. 获取循环单链表的长度

​ 获取循环单链表,每经过一个节点,链表长度加一,获取链表长度,代码实现如下:

def length(self):
  size = 0
  if 域名y():
    return size
  cur = 域名
  # cur 指向当前链表的第一个元素,即当前链表的长度从第一个元素开始计算,长度 +1
  size += 1
  # cur的后继节点不是头结点,即cur不指向尾节点
  while 域名 is not 域名:
    size += 1
    cur = 域名
  return size

3. 尾插入法

  • 链表为空,将表头、表尾指针指向新节点
  • 链表不为空,将尾节点的直接后继指针指向新节点,新节点的后继指针指向头节点,如图所示:

image

代码实现如下:

def append(self, val):
  \'\'\'
  尾插法
  :param val:待插入的关键字
  \'\'\'
  newNode = Node(val)
  if 域名y():
    域名 = newNode
    域名 = newNode
  else:
    域名 = newNode
    域名 = newNode
    域名 = 域名    

4. 删除节点

  • 链表为空,无法删除,抛出异常
  • 链表不空且只有一个节点,将头尾指针指向空
  • 链表不空且有多个节点,将链表中的导数第二个节点的直接后继指向头结点,将尾节点的直接后继指向空

如图所示:

image

代码如下:

def delete(self):
  \'\'\'
  删除节点
  \'\'\'
  if 域名y():
    raise IndexError(\'链表为空\')
  if 域名th() == 1:
    域名 == None
    域名 = None
  else:
    cur = 域名
    temp = 域名
    # 遍历链表,找到导数第二个节点
    while 域名 is not 域名:
      cur = 域名
      temp = 域名
    # 将尾指针指向导数第二个节点,使之成为新的尾节点
    域名 = cur
    # 新的尾节点直接后继指向表头节点
    域名 = 域名  

5. 查找节点

 在链表中查找数值为key的节点,从头节点向后遍历,判断是否有节点的值等于key,若有,则查找成功,否则查找失败,代码实现如下:

def find(self, key):
  \'\'\'
  查找某个关键字
  :param  key: 关键字
  :return : 存在则返回True,否则返回False
  \'\'\'
  cur = 域名
  while 域名 != 域名:
    if key == 域名:
      return True
    cur = 域名
  return False

6. 遍历循环链表

 若链表为空,则无法遍历链表,退出程序;若链表不为空,则从表头开始向后遍历,并且打印节点的数据域,代码实现如下。

def travesal(self):
  \'\'\'
  遍历循环链表
  \'\'\'
  if 域名y():
    raise IndexError(\'链表为空\')
  cur = 域名
  # 当cur的直接后继指针不是指向头结点是时,即cur尚未指向尾节点时
  while 域名 is not 域名:
    print(域名, end = \' \')
    cur = 域名
  # 打印尾节点
  print(域名)

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。