【算法导论】笔记-第九章 基本数据结构

第9章 基本数据结构

9.1 栈与队列

    • 栈是限定在一端进行插入和删除的线性表。

    • 操作:INSERT称作压入(PUSH),DELETE称作弹出(POP)

    • 特点:

      • 只能在栈顶进行插入和删除
      • 先进后出, 后进先出
      • 栈底指针 bottom ,栈顶指针 top
      • 栈底指针不变,栈中元素随栈顶指针的变化而动态变化
      • 栈具有记忆功能
      • 栈支持子程序调用
    • 伪代码:

      • STACK-EMPTY(S):测试是否为空栈

        if S.top == 0
            return TRUE
        else return FALSE
        
      • PUSH(S, x)

        S.top = S.top + 1
        S[S.top] = x 
        
      • POP(S)

        if STACK-EMPTY(S)
            error "underflow"
        else S.top = S.top - 1
            return S[S.top + 1]
        
    • 运行时间: O ( 1 ) O(1) O(1)

  • 队列

    • 队列是指允许在一端进行插入,而在另一端进行删除的线性表。

    • 操作:INSERT称作入队(ENQUEUE),DELETE称作出队(DEQUEUE)

    • 特点:

      • 队列只允许在队尾进行插入,而在队头进行删除
      • 先进先出 ,后进后出
      • 队头指针 front ,队尾指针 rear
      • 队列中元素随队头指针和队尾指针的变化而动态变化
    • 循环队列:计算:

      • rear>front: s=rear-front
      • rear
      • rear=front: s=1 或者 s=0
    • 伪代码:

      • ENQUEUE(Q, x)

        Q[Q.tail] = x
        if Q.tail == Q.length
            Q.tail = 1
        else Q.head = Q.head + 1
        return x
        
      • DEQUEUE(Q)

        x = Q[Q.head]
        if Q.head = Q.length
            Q.head = 1
        else Q.head = Q.head + 1
        return x
        
    • 运行时间: O ( 1 ) O(1) O(1)

9.2 链表

  • 双向链表:每个元素都是一个对象,每个对象都有一个关键字 k e y key key和两个指针: n e x t 和 p r e v next和prev nextprev

    • x . n e x t x.next x.next:指向它在链表中的后继元素
    • x . p r e v x.prev x.prev:指向它在链表中的前驱元素
  • 链表: x . p r e v = N I L x.prev=NIL x.prev=NIL,即没有前驱

    • 链表的头 h e a d head head:链表的第一个元素
    • 链表的尾 t a i l tail tail:链表的最后一个元素
    • 特点:
      • L . h e a d = N I L L.head=NIL L.head=NIL,则链表为空
      • 各数据结点的存储空间可以不连续
      • 各数据元素的存储顺序和逻辑循序可以不一致
      • 线性表的链式存储所占存储空间大于顺序存储结构
      • 查找结点时链式储存要比顺序存储慢
      • 链式存储插入删除元素比顺序存储灵活
    • 线性链表的操作:在线性链表中进行插入与删除,不需要移动链表中的元素。
    • 类别:
      • 单链接:省略每个元素的 p e r v perv perv指针
      • 双链接:双向链表
      • 已排序:链表的线性顺序与元素中关键字的线性顺序一致
      • 未排序:元素无顺序排列
  • 链表的搜索:

    • 采用简单的线性搜索方法

    • 伪代码:LIST-SEARCH(L, k)

      x = L.head
      while x != NIL and x.key != k
          x = x.next
      return x
      
    • 最坏情况运行时间: O ( n ) O(n) O(n)

  • 链表的插入

    • 伪代码:LIST-INSERT(L, x)

      x.next = L.head
      if L.head != NIL
          L.head.prev = x
      L.head = x
      x.prev = NIL
      
    • 运行时间: O ( 1 ) O(1) O(1)

  • 链表的删除

    • 伪代码:LIST-DELETE(L, x)

      if x.prev != NIL
          x.prev.next = x.next
      else L.head = x.next
      if x.next != NIL
          x.next.prev = x.prev
      
    • 运行时间: O ( 1 ) O(1) O(1)

  • 哨兵

    • 哨兵是一个哑对象,其作用是简化边界的处理
    • 假设在链表L中设置一个对象 L . n i l L.nil L.nil,该对象代表NIL,但具有其他对象相同的各个属性。对于链表代码中出现的每一处对NIL的引用,都代之以对哨兵 L . n i l L.nil L.nil的引用。这样调整将一个常规的双向链表转变成一个有哨兵的双向循环链表,哨兵L.nil位于表头和表尾之间。
    • 属性L.nil.next指向表头,L.nil.prev指向表尾。同样表尾的next属性和表头的prev属性同时指向L.nil。
    • 因为L.nil.next指向表头,我们就可以去掉属性L.head,并把对它的引用代替为对L.nil.next的引用。
    • 一个空的链表只由一个哨兵组成,L.nil.next和L.nil.prev同时指向L.nil。

9.3 指针和对象的实现

  • 对象的多数组表示:

    • 对一组具有相同域的对象,每一个对象都可以用一个数组来表示 。
    • 对于一个给定的数组下标x,三个数组项 k e y [ x ] , n e x t [ x ] , p r e v [ x ] key[x], next[x], prev[x] key[x],next[x],prev[x]一起表示链表中的一个对象。
  • 对象的单数组表示:一个对象占据存储中的一组连续位置 。

  • 对象的分配与释放:使用垃圾收集器来确定链表中哪些对象空间未被使用

9.4 有根树的表示

  • 二叉树:利用属性 p , l e f t , r i g h t p, left, right p,left,right存放指向父节点,左子叶,右子叶的指针。属性 T . r o o t T.root T.root指向整棵树的根结点。
  • 分支无限制的有根树:将二叉树的表示方法推广到多个结点的任意类型的树
    • 左兄弟右兄弟表示法:减少存储空间的浪费
    • 只使用两给指针:
      • x . l e f t − c h i l d x.left-child x.leftchild指向结点x最左边的根结点
      • x . r i g h t − c h i l d x.right-child x.rightchild指向x右侧相邻的根结点

你可能感兴趣的:(算法导论,链表,队列,数据结构,算法导论,算法)