第三部分--数据结构-第10章--基本数据结构

说明:该系列博客整理自《算法导论(原书第二版)》,但更偏重于实用,所以晦涩偏理论的内容未整理,请见谅。另外本人能力有限,如有问题,恳请指正!

    基本数据结构包括:栈、队列、链表、有根树。

1、栈

    栈和队列都是动态集合。栈实现了一种 后进先出(LIFO) 的策略,队列实现了一种 先进先出(FIFO) 的策略。栈操作的时间复杂度为O (1)。

    作用于栈上的 INSERT 操作称为 压入 ( PUSH ),而无参的 DELETE 操作常称为 弹出 ( POP )。可以使用一个数组 S [ 1 .. n ]来实现一个至多有 n 个元素的栈。数组 S 有个属性 S.top ,它指向最近插入的元素。由S 实现的栈包含元素S[1...S.top],其中S[1]是栈底元素,S[S.top]是栈顶元素。当S.top=0时栈中不包含任何元素,此时栈为空栈。如果试图对一个空栈做弹出操作,则称栈“下溢”。如果S.top>n,则称栈“上溢”。

STACK-EMPTY(S)

1 if S.top == 0//注意S.top的值是从1开始的

2    return TRUE

3 else

4    return FALSE

PUSH(S, x)

1 S.top = S.top + 1

2 S[S.top] = x

POP(S)

1 if STACK-EMPTY(S)

2    error "underflow"

3 else

4    S.top = S.top - 1

5 return S[S.top + 1]

2、队列

    栈和队列都是动态集合。栈实现了一种 后进先出(LIFO)的策略,队列实现了一种 先进先出(FIFO)的策略。队列操作的时间复杂度为O(1)。

    我们把作用于队列上的 INSERT 操作称为 入队 ( ENQUEUE ),把作用于队列上的 DELETE 操作称为 出队 ( DEQUEUE )。队列有  和  。当一个元素入队时,将排在队尾,而出队的元素总是队首元素。

    用一个数组 Q [ 1 .. n ]来实现一个至多含 n - 1 个元素的队列。队列具有属性 Q.head ,它指向队列的头,另一个属性为 Q.tail ,它指向新元素将会被插入的地方。队列需要在最后一个位置进行“卷绕”,即队列中的n个元素形成环形,位置1接在位置n之后。当Q.head = Q.tail时队列为空,当Q.head = Q.tail+1时队列为满。下面列出队列的常用操作,但是省略了溢出检查部分。

ENQUEUE(Q, x)

1 Q[Q.tail] = x  //Q.tail 指向新元素将会被插入的地方

2 if Q.tail == Q.length

3    Q.tail = 1

4 else

5    Q.tail = Q.tail + 1

DEQUEUE(Q)

1 x = Q[Q.head]

2 if Q.head == Q.length

3    Q.head = 1

4 else

5    Q.head = Q.head + 1

6 return x

3、无哨兵链表

    在 链表 中,各对象按线性顺序排序。其顺序由各对象中的指针决定。本节介绍的是无序的双链表。 双链表 的每一个元素都是一个对象,每个对象包含一个关键字域和两个指针域: next 和 prev 。对链表中的某个元素 x , x.next 指向链表中 x 的后继元素,而 x.prev 则指向链表中 x 的前驱元素。如果x.prev=nil,则它是链表的第一个元素;如果x.next=nil,则它是链表的最后一个元素。L.head指向链表的第一个元素,如果L.head=nil,则链表为空。下面给出的是链表的基本操作。

    查找操作的最坏时间复杂度为Θ(n),插入操作运行时间为O (1),删除操作运行时间为O(1)。

LIST-SEARCH(L, k)

1 x = L.head

2 while x != NIL and x.key != k

3    x = x.next

4 return x

LIST-INSERT(L, x)

1 x.next = L.head

2 if L.head != NIL

3    L.head.prev = x

4 L.head = x

5 x.prev = NIL

LIST-DELETE(L, x)

1 if x.prev != NIL

2    x.prev.next = x.next

3 else

4    L.head = x.next

5 if x.next != NIL

6    x.next.prev = x.prev

4、哨兵链表=对无哨兵链表的改进

    哨兵可以简化边界条件。例如,假设有链表L和一个对象L.nil,后者表示NIL,但也包含和其他元素一样的各个域。将链表算法中出现的每次对NIL的引用,用哨兵元素L.nil的引用来代替,这样,就可以将一个一般的双向链表变成一个带哨兵的环形双向链表,哨兵元素L.nil介于头和尾之间。L.nil.next指向链表的头,L.nil.prev指向链表的尾,因为L.nil.next指向链表的头,所以去掉了L.head属性。一个空链表仅包含哨兵元素,此时L.nil.next=nil,且L.nil.prev=nil。

LIST-DELETE(L, x)

1 x.prev.next = x.next

2 x.next.prev = x.prev   

LIST-SEARCH(L, k)

1 x = L.nil.next

2 while x != L.nil and x.key != k

3    x = x.next

4 return x

LIST-INSERT(L, x)

1 x.next = L.nil.next

2 L.nil.next.prev = x

3 L.nil.next = x   

4 x.prev = L.nil

    哨兵元素基本上不能降低施于有关数据结构上的各种操作的渐近时间界,但他们可以降低常数因子。在循环结构中使用哨兵的好处主要有两方面:一是使得代码更加简洁,但对于速度则没有什么帮助;二是可以使循环部分的代码更加紧凑,因而可以降低运行时间中项n或项n2的系数。

    要注意不能不加区别的使用哨兵。如果有很多较短的链表,则使用哨兵后就会造成存储的浪费。在本书中,当哨兵真正能简化代码时,我们才加以采用。

5、有根树的表示

    在这一节中,我们来讨论用链接数据结构表示有根树的问题。首先,要讨论二叉树,然后提出一种适用于结点子女数任意的有根树表示方法。

    我们用一个对象来表示树的一个结点。对链表,假设每个结点都有一个关键字域,其余域包括指向指向其他结点的指针,而且要根据不同类型的树而发生变化。

    1)、二叉树

    如下图所示,用域 p , left , right 来存放指向二叉树 T 中的父亲,左儿子和右儿子的指针。如果 x.p = NIL ,则 x 为根。如果结点 x 无左儿子,则 x.left = NIL ,对右儿子也类似。整个树 T 的根由属性 T.root 指向。如果 T.root = NIL ,则树为空。

    2)、分支数无限的有根树

    上面二叉树的表示方法可以推广至每个结点的子女数至多为常数k的任意种类的树:用chaild1...childk来取代left和right域。如果树中结点的子女数是无限制的,那么这种方法就不适用了,因为我们无法事先知道有多少域需要分配。此外,即使结点子女数k以一个很大的常数为界,但多数结点只有少数子女,则会浪费大量的存储空间。

    值得庆幸的是,可以用二叉树很方便地表示具有任意子女数的树。该方法的优点是对任意含 n 个结点的有根树仅用 O ( n )空间。这种 左孩子 , 右兄弟 的表示如下图所示。每个结点都包含一个父亲指针 p , T.root 指向树 T 的根。每个结点 x 不再包含指向每个孩子结点的指针,而仅包含两个指针:

            a、x.left-child 指向结点 x 的最左孩子。

            b、x.right-sibling 指向结点 x 紧右边的兄弟。

zai he

    如果 x 没有孩子,则 x.left-child = NIL ;如果 x 是其父结点的最右孩子,则 x.right-sibling = NIL 。

    3、树的其他表示

    有时,可以用另外一些方法来表示有根树。例如,在第6章中,我们用了一个数组加上下标的形式来表示基于完全二叉树的堆。将在21章中出现的树只可由页向根的方向遍历,故只用到父指针,而没有指向子女的指针。另外,还有很多其他的方法,哪一种最好要视具体的应用而定。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容