Python描述:数据结构与算法 2.顺序表

2.1 线性表的概念和性质

2.1.1 线性表是 E E E 中有穷个元素的组成的序列 L = ( e 0 , e 1 , . . . , e n − 1 ) L =(e_{0},e_{1},...,e_{n-1}) L=(e0,e1,...,en1) ,元素之间的关系 为一个二元组的集合 R = { < e 0 , e 1 > , < e 1 , e 2 > , . . . , < e n − 2 , e n − 1 > } R=\{ <e_{0},e_{1}>,<e_{1},e_{2}>,...,<e_{n-2},e_{n-1}> \} R={<e0,e1>,<e1,e2>,...,<en2,en1>}

2.1.2 对于非空的线性表或者线性结构的特点:

  • 存在唯一一个首元素
  • 存在唯一一个尾元素
  • 除首元素外,表中的每一个元素都有且仅有一个前驱元素
  • 除尾元素外,表中的每一个元素都有且仅有一个后继元素

2.1.3 线性表的抽象数据类型ADT

2.1.4 线性表的两种存储结构(实现方式):

  • 将表中元素顺序的存储地存放在一块连续的存储区中,即顺序表
  • 将表中元素存放在通过链接构造起来的一系列存储区中,即链接表
2.2 顺序表

2.2.1 顺序表中元素之间的逻辑关系通过元素在存储区中的物理位置表示(隐式关系),元素的物理位置(内存地址)表示为 : L o c ( e i ) = L o c ( e 0 ) + s i z e ( e ) × i Loc(e_{i})=Loc(e_{0})+size(e)\times i Loc(ei)=Loc(e0)+size(e)×i

2.2.2 顺序表中元素存储结构:

  • 顺序结构(用于元素大小相同时)
  • 索引结构(用于元素大小不同时)

2.2.3 顺序表全局信息的存储方式:

  • 一体式结构(顺序结构)(静态顺序表)

  • 分离式结构(链接结构)(动态顺序表)

2.2.4 顺序表基本操作的实现:

  • 创建空表

    const int max = 100;  //表容量,为定值
    int num = 0;    //当前表中元素个数
    int list[max];  //实际上表中元素大小可能不同
    
  • 判断表状态 O ( 1 ) O(1) O(1)

    if (num==0) 
        empty();  //表空
    else if (num==max) 
        empty();  //表满
    
  • 访问下标 i i i 元素 O ( 1 ) O(1) O(1)

    if (0<=i && i<=num=-1) 
        empty();  //非法访问
    else 
        return list[i];
    
  • 遍历 O ( n ) O(n) O(n)

    int i=0;
    for (i;i<num;i++) {
        empty();
    }
    
  • 尾端插入元素 O ( 1 ) O(1) O(1)

    if (num==max)  empty();  //表满无法插入
    else {
        list[num]=n;  
        num++;
    }
    
  • 插入元素到第 i i i个位置

    if (num==max)  
        empty();  //表满无法插入
    else if (0<=i && i<num)  
    
    • 不保序 O ( 1 ) O(1) O(1)

      {
          list[num]=list[i];  
          list[i]=n;  
          num++;
      }  //将原有元素移到表尾
      
    • 保序 O ( n ) O(n) O(n)

      {
          int j=num;
      	do {
          	list[j]=list[j-1];  
          	j--;
          }  while(j>i); 
          list[i]=n;  
          num++;
      }  //将i后元素都顺移一位  
      

  • 删除尾端元素 O ( 1 ) O(1) O(1)

    num--;
    
  • 删除位置 i i i 的元素

    if (0<=i && i<num)  
    
    • 不保序 O ( 1 ) O(1) O(1)

      {
          list[i]=list[num-1];
          num--;
      }  //将表尾元素移到空缺位置
      
    • 保序 O ( n ) O(n) O(n)

      {
       int j=i;
         do {
       	list[j]=list[j+1];  
       	j++;
       }  while(j<num); 
       num--; 
      }    //将i后元素都前移一位  
      

2.2.5 元素存储区扩充,扩充的两种策略:

  • 每次扩充增加固定数目的存储位置(线性增长)

    特点:节省空间,但是扩充操作频繁,操作次数多。

  • 每次扩充容量加倍(指数增长)

    特点:减少了扩充操作的执行次数,但可能会浪费空间资源。以空间换时间,推荐的方式。

2.3 链接表

2.3.1 链接表的实现方式:

  • 将表中的元素分别存储在一批独立的存储块里
  • 表结构中的结点都能找到与之相关的下一个结点
  • 在前一结点里用链接的方式显式的记录与下一节点之间的关联
2.3.2 单向链表

2.3.2.1 单链表的结点可用一个二元组来表示 ( e l e m , n e x t ) (elem,next) (elemnext) e l e m elem elem保存着表的数据项, n e x t next next存储着下一个结点的标识,则单链表由表节点和指向表头的表头变量(表头指针) h e a d head head组成。

2.3.2.2 为了表示链表的结束,则需要给最后节点的链接域设置一个空连接。

2.3.2.3 在Python中定义一个表节点类:

class LNode:
    def __init__(self,elem,next_=None):
        self.elem = elem
        self.next = next_

2.3.2.4 单链表基本操作的实现:

  • 创建空链表

    在Python中定义一个单链表类:

    class LList:
        def __init__(self):
            self._head = None
    
  • 判断表状态 O ( 1 ) O(1) O(1)

    if self.head == None:
        pass  #表表空
              #链表一般不会满,除非存储空间不足
    
  • 删除链表 O ( 1 ) O(1) O(1)

    self.head = None
    
  • 表首插入元素 O ( 1 ) O(1) O(1)

    self.head = LNode(elem,self.head)  #将表头指针指向新元素,新元素链接原表头元素
    
  • 尾端插入 O ( n ) O(n) O(n)

    if self._head = None:  #表空直接插入
        self._head = LNode(elem)
        return
    p = self._head
    while p.next == None:  #将指针不断后移直至最后一个元素
        p = p.next
    p.next = LNode(elem)
    
  • 在第 i i i个元素后定位插入元素 O ( n ) O(n) O(n)

    if i == 0:  #表首插入
        self.head = LNode(elem,self.head) 
     	return 
    q = LNode(elem)
    p = self._head
    cnt = 1
    while p is not None and cnt<i:  #指针不断后移直到第i个元素
        p = p.next
        cnt += 1
    if p is None:  #i不符合范围
        raise Error
    else:
        q.next = p.next
        p .next = q
    
  • 删除表首元素 O ( 1 ) O(1) O(1)

    if self._head = None:
        raise Error
    self._head = self._head.next
    
  • 删除表尾元素 O ( n ) O(n) O(n)

    if self._head = None:
        raise Error
    p = self._head
    if p.next is None:  #表中只有一个元素
        self._head None
        return  
    while p.ext.next is not None:
        p = p.next
    p.next = None
    
  • 删除第 i i i个元素 O ( n ) O(n) O(n)

    if i<0:
        raise Error
    q = LNode(elem)
    p = self._head
    cnt = 1
    while p is not None and cnt<i:  #指针不断后移直到第i个元素
        p = p.next
        cnt += 1
    if p is None:  #i不符合范围
        raise Error
    else:
        p.next = p.next.next
    
  • 遍历 O ( n ) O(n) O(n)

    p = self._head
    while p.next is not None:
        pass
        p = p.next
    
  • 定位

    • 按下标定位第 i i i个元素 O ( n ) O(n) O(n)

      p = self._head
      cunt = 1
      while p is not None and cnt<i:
          p = p.next
          cnt += 1
      if p is None:
          raise
      else:
          return p.elem
      
    • 按谓词定位 O ( n ) × T O(n)\times T O(n)×T

      p = self._head
      while p is not None:
          if pred(p.elem):
              return
          p = p.next
      

2.3.2.4 单链表的简单变形

  • 增加表尾指针 r e a r rear rear
  • 循环单链表(即其中最后结点的 next 指向首结点)
2.3.3 双向链表

2.3.3.1 在单链表节点的基础上,扩充一个指向前一节点的链接域 p r e v prev prev

2.3.3.2 继承单链表节点类创建一个双链表节点,双链表类仍然只需要一个头部指针 h e a d head head则可,可基于单链表类派生。

class DLnode(LNode):
    def __init__(self,elem,prev=None,next_=None)
    LNode.__init__(self,elem,next_)
    self.prev = prev

2.3.3.2 也可以根据需要,对双链表做一些变形,例如循环双链表

2.4 总结

2.4.1 不同链接表的简单总结

  • 单链表:只有一个方向的链接,支持 O ( 1 ) O(1) O(1) 的前端插入和删除,定位操作或尾端操作都需要$O(n) $时间

  • 带尾结点引用的单链表:可很好支持前端/尾端插入和前端弹出,都是 O ( 1 ) O(1) O(1)时间复杂度的操作,但不能支持高效的尾端删除

  • 循环单链表:支持高效前端/尾端插入和前端弹出,扫描需注意结束判断

  • 双链表:中结点有两个方向的链接,能高效找到前后结点。有尾结点引用时两端插入和删除都是 O ( 1 ) O(1) O(1) 时间操作。循环双链表的性质类似

2.4.2

  • 优点:
    • 表结构是通过一些链接起来的结点形成的,结构很容易调整修改
    • 只通过修改链接,就能灵活地修改表的结构和内容。如加入/删除一个或多个元素,翻转表,重排元素顺序,将表分解为两个或多个等
  • 缺点
    • 一些操作的开销大:
      • 基于位置找到表中元素需要线性时间
      • 尾端操作需要线性时间(增加尾指针可以将尾端加入元素变为常量操作,但仍不能有效实现尾端删除)
      • 找当前元素的前一元素需要从头扫描表元素(双链表可以解决这个问题,但每个结点要付出更多存储代价)
    • 每个元素增加了一个链接域(存储代价),双链表增加两个链接域
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值