【以练促学:数据结构】2.线性表

(持续刷题,持续更新...)

1.顺序表与链表的比较

(空间性能) 顺序表 链表  
  顺序存储结构 链式存储结构  
  逻辑相邻,物理存储位置相邻 逻辑相邻,物理存储位置未必相邻 顺序表必须是连续物理存储空间
存储空间分配 必须预先分配 不用预先分配  
存储密度 1 小于1 顺序表的存储密度更大

 

(时间性能) 顺序表 链表  
  随机存取 顺序存取  
存储元素效率 O(1) O(n)  
插入/删除操作的效率 O(n) O(1) 链表的 插入/删除操作更方便
查找操作的效率 O(1) O(n) 顺序表的 查找操作更方便

 

 eg:下列(  )是顺序存储结构的优点

A. 存储密度大

B. 插入运算方便

C. 删除运算方便

D. 方便地运用于各种逻辑运算的存储表示

// 顺序表存储密度大(不用在结点中存放指针),D中,对于树形结构还是链式的更方便

 

 eg:线性表的顺序存储结构是一种(  )

A. 随机存取的存储结构

B. 顺序存取的存储结构

C. 索引存取的存储结构

D. 散列存取的存储结构

 // 存取方式指读写方式,顺序表随机存取,即根据其下标可以很方便的访问任意元素

 

 eg:对于一个线性表,既要求能够进行较快的插入和删除,又要求存储结构能够反映数据间的逻辑关系,则应该用(  )

A. 顺序存储方式

B. 链式存储方式

C. 散列存储方式

D. 以上均可

 // 顺序存储方式(就是一个个找),不能较快插入/删除(要移后面的数据);散列存储通过散列函数映射到物理空间,不能反映数据间逻辑关系;链式存储方式两个都能满足:既能较快的插入/删除(O(1)),又能表示各种逻辑关系

 

 eg:若线性表最常用的操作是存取第 i 个元素及其前驱和后继元素的值,为了提高效率,应采用(  )的存储方式

A. 单链表

B. 双向链表

C. 单循环链表

D. 顺序表

 // ABC链表都只能从头开始依次顺序查找 O(n),顺序表可以直接根据下标随机存取 O(1)

 

 eg:若线性表最常用的操作是存取第 i 个元素及其前驱的值,则采用(  )存储方式节省时间

A. 单链表

B. 双向链表

C. 单循环链表

D. 顺序表

 // 要取前驱,首先排除单链表,而在双链表和顺序表中,顺序表的随机存取又优于双链表

 

eg:在n个元素的线性表的数组表示中,时间复杂度为 O(1) 的操作(  )(多选)

A. 访问第 i(1<= i <= n)个结点和求第 i(2<= i <= n)个结点的直接前驱

B. 在最后一个结点后插入一个新的结点

C. 删除第1个结点

D. 在第 i(1<= i <= n)个结点后插入一个结点

// 线性表随机存取,访问第 i 和 i-1 的结点为 O(1);在尾巴上新增(不用移动结点)也是 O(1);删除和插入结点后,均需要依次移动后面的结点,故为 O(n);

 

eg:设线性表有 n 个元素,严格来说,以下操作中,(  )在顺序表上实现要比链表上实现效率高(多选)

A. 输出第 i(1<= i <= n)个元素值

B. 交换第3个元素与第4个元素的值

C. 顺序输出这 n 个元素的值

// A顺序表 O(1),链表 O(n);B顺序表中交换元素要找个中间商,进行3次交换;而链表要分别找到3/4结点的前驱,第4个结点断链后接到2后面(比较复杂);C顺序输出时顺序表和链表均 O(n)

 

eg:对于顺序表,访问第 i 个位置的元素,和在第 i 个位置插入一个元素的时间复杂度分别为(  )

A. O(n),O(n)

B. O(n),O(1)

C. O(1),O(n)

D. O(1),O(1)

// 顺序表的访问是随机存储 O(1),插入元素需要后面的元素给它腾位置,所以是 O(n)

 

eg:单链表的存储密度(  )

A. 大于1

B. 等于1

C. 小于1

D. 不确定

// 存储密度指:数据元素本身所占存储量:整个结点结构所占存储量;链表结点要额外放置指针域,所以存储密度一定小于1

 

eg:对于单链表表示法,以下说法错误的是(  )

A. 数据域用于存储线性表的一个数据元素

B. 指针域或链域用于存放一个指向本结点的直接后继结点的指针

C. 所有数据通过指针的链接而组织成单链表

D. NULL称为空指针,它不指向任何结点,只起标志作用

// “所有数据通过指针的链接而组织成单链表”,应该是所有结点(包括数据、指针)通过指针的链接而组织成单链表

 

2.线性表元素序号从1始,下标索引从0始

eg:若长度为 n 的非空线性表采用顺序存储结构,在表的第 i 个位置插入一个数据元素,则 i 的合法值应该是(  )

A. 1<= i <= n

B. 1<= i <= n+1

C. 0<= i <= n-1

D. 0<= i <= n

// 线性表元素的序号是从1开始的,而在第 n+1 的位置相当于在表尾追加

 

 3. 静态链表

  •  静态链表要预先分配一块连续的内存空间
  • 静态链表借助数组来描述线性表的链式存储结构(非随机存取)
  • 静态链表的指针是结点的相对地址(数组下标),又称游标
  • 静态链表的插入/删除操作与动态链表相似:只需修改指针,不用移动元素

 

eg:以下关于线性表说法正确的是(  )(多选)

A. 顺序存储方式只能用于存储线性结构

B. 取线性表的第 i 个元素的时间与 i 的大小有关

C. 静态链表需分配较大的连续空间,插入和删除操作不需要移动元素

D. 在一个长度为 n 的有序单链表中插入一个新结点并仍保持有序的时间复杂度是O(n)

E. 若用单链表来表示队列,则应选用带尾指针的循环链表

// 顺序存储方式除了适用于存储线性结构,还有图和树;线性表包括顺序表和链表,顺序存储时是随机存取的和 i 无关;静态链表需分配较大的连续空间,插入和删除操作不需要移动元素;队列(头删尾进)采用带尾指针的循环链表较方便,插入/删除的时间复杂度为 O(1)

 

eg:静态链表中的指针表示(  )

A. 下一元素的地址

B. 内存储器地址

C. 下一个元素在数组中的位置

D. 左键/右键指向的元素地址

// 静态链表中的指针又称游标,指示下一个元素在数组中的下标

 

eg:需要分配较大空间,插入和删除不需要移动元素的线性表,其存储结构为(  )

A. 单链表

B. 静态链表

C. 顺序表

D. 双链表

// 静态链表用数组表示,因此需要预先分配较大的连续空间;同时,静态链表的插入/删除操作与动态链表相似:只需修改指针,不用移动元素

 

4. 链表的时间复杂度

  • 单链表的查找/建立 :O(n)
  • 数组排序(折半查找)复杂度O(nlog2n)

eg:给定有 n 个元素的一维数组,建立一个有序单链表的最低时间复杂度为(  )

A. O(1)

B. O(n)

C. O(n2)

D. O(nlog2n)

// “有序单链表”就是有顺序的单链表,有两种实现方式:①先建立,再依次插入(每次插入一个元素就需要遍历以寻找插入位置),时间复杂度为:O(n2);②先给数组排序O(nlog2n),再建立链表 O(n),总时间复杂度为:O(nlog2n);综上,O(n2) 和 O(nlog2n) 中最低的是 O(nlog2n) 

 

eg:线性表(a1,a2,...,an)以链接方式存储时,访问第 i 个位置上元素的时间复杂度为(  )

A. O(i)

B. O(n)

C. O(n2)

D. O(nlog2n)

// 可以理解为,时间复杂度是算法问题规模 n 的函数,时间复杂度主要是分析 T(n) 的数量级

 

5. 合并表

  • (无条件合并)A+B:遍历到链表A 的尾节点,然后将 next 域指向链表B 的首结点,时间复杂度 O(A)
  • (有序表合并)A+B
    • 合并后按原序:最好情况是一个表中元素全部小于另一表,时间复杂度 O(min(A,B));最坏情况是两表元素交替,时间复杂度 O(A+B)
    • 合并后按原序的逆序排列:最好/坏情况的时间复杂度都是 O(A+B)

 

eg:将长度为 n的单链表链接在长度为m的单链表后面,其算法的时间复杂度为(  )

A. O(1)

B. O(n)

C. O(m)

D. O(n+m)

// “将长度为n的单链表链接在长度为 m 的单链表后面”就是:m+n,遍历到 m 的尾节点,然后将 next 域指向另一个表的首结点,时间复杂度为 O(m)

 

eg:已知两个长度分别为 m 和 n 的升序链表,若将它们合并为一个长度为 m+n 的降序链表,则最坏情况下的时间复杂度为(  )

A. O(n)

B. O(m*n)

C. O(min(m,n))

D. O(max(m,n))

// “将升序链表合并为降序”,最好/坏情况的时间复杂度都是 O(m+n),所以很奇怪的就是O(max(m,n))

 

 

 

 6. 链表基础操作

  • 带头结点/不带头结点:
    • 有头:第一个结点不保存数据,区别于其他数据结点(更方便插入/删除操作)
    • 无头:第一个结点直接就存数据了

  • 插入:
  • 删除:
    • (单链表)从头结点开始顺序查找到其前驱,再执行删除,O(n)
    • (单链表)直接覆盖 ,O(1),但要注意尾指针是特殊情况(尾指针不能指空)
    • 一般用单链表删除尾结点元素很不方便(建议用双链表),删除尾结点要从头遍历到尾结点的前驱结点,并置空它
  • 判空:
    • (带头结点)head->next==NULL
    • (不带头结点)head==NULL

 

eg:在一个长度为 n 的带头结点的单链表 h 上,设有尾指针 r,则执行(  )操作与链表的表长有关

A. 删除单链表中的第一个元素

B. 删除单链表中的最后一个元素

C. 在单链表第一个元素前插入一个新元素

D. 在单链表最后一个元素后插入一个新元素

//  “删除单链表的最后一个结点”,需要置空其前驱结点的指针域为NULL(要从头遍历),需要O(n)的时间,与表长相关

 

eg:若一个链表最常用的操作是在末尾插入和删除结点,则选用(  )最节省时间

A. 带头结点的双循环链表

B. 单循环链表

C. 带尾指针的单循环链表

D. 单链表

// 根据删除尾结点的特殊性(单链表要从头遍历),建议选择双链表

 

eg:设对 n(n>1) 个元素的线性表的运算只有4种:删除第一个元素,删除最后一个元素,在第一个元素之前插入新元素,在最后一个元素之前插入新元素;则最好使用(  )

A. 只有尾结点指针,没有头结点指针的循环单链表

B. 只有尾结点指针,没有头结点指针的非循环双链表

C. 只有结点指针,没有尾结点指针的循环双链表

D. 既有结点指针,又有尾结点指针的循环单链表

// 根据 “删除最后一个元素” 的特殊性(单链表要从头遍历),建议选择双链表;而 B 没有头,“删除第一个元素” 时要从尾遍历一遍到头

 

eg:已知表头元素为 c 的单链表在内存中的存储状态如下表:现将 f 存放于 1014H 处并插入单链表,若 f 在逻辑上位于 a 和 e 之间,则 a,e,f的“链接地址”依次是(  )

地址 元素 链接地址
1000H a 1010H
1004H b 100CH
1008H c 1000H
100CH d NULL
1010H e 1004H
1014H    

 

A. 1010H,1014H,1004H

B. 1010H,1004H,1014H

C. 1014H,1010H,1004H

D. 1014H,1004H,1010H

// 对应 “地址” 和 “链接地址” 发现 c 的 “地址”没有一个元素的 “链接地址” 接,所以 c 是头,该链表顺序:(1008H)c -> 去找(1000H) -> (1000H)a -> 去找(1010H) -> (1010H)-> 去找(1004H) -> (1004H)b -> 去找(100CH) -> (100CH)d -> NULL;此时要在 a,e 间插入 f:①(1000H)a -> 去找(1010H) -> (1010H)-> 去找(1004H);②用 1014H 存 f ;③ f 指向(1010H)e,a指向(1014H) f ;所以综上就变成:(1000H) (1014H) -> (1014H) (1010H) -> (1010H) (1004H),所以a,e,f的“链接地址”依次是:1014H,1004H,1010H

  

eg:对于一个头指针为 head 的带头结点的单链表,判空条件为(  );

        对于一个不带头结点的单链表,判空条件为(  );

A. head == NULL;

B. head -> next == NULL;

C. head -> next == head;

D. head != NULL;

// (带头)头指针 head 指向头节点,头结点的 next 域指向(head -> next)第一个元素结点:head -> next == NULL;

//(不带头)头指针 head 直接指向(head ==)第一个元素结点:head == NULL;

 

eg:在一个以 L 为头指针的单循环链表中,p 指针指向链尾的条件是(  );

A. p -> next == L;

B. p -> next == NULL;

C. p -> next -> next == L;

D. p -> data== -1;

// 单循环链表的链尾不是空啊啊,是循环链表啊啊,又指回到头了 p -> next -> next == L;

 

eg:在循环链表中,将头指针改设为尾指针(rear)后,其首元结点和尾结点的存储位置分别为(  );

A. rear 和 rear -> next -> next 

B. rear -> next 和 rear

C. rear -> next -> next 和 rear

D. rear 和 rear -> next

// 将头指针改设为尾指针(rear)” 就是将指向头的指针,指向尾,首元结点就是rear -> next -> next (头 -> next );尾结点就是指针的位置 rear

 

eg:向一个有127个元素的顺序表中插入1个新元素,并保持原来顺序不变,平均要移动的元素个数为(  );

A. 8

B. 63.5

C. 63

D. 7

// 顺序表有127个元素,枚举一下:假如在第一个位置上插入,则要移动127;在第二个位置上插入,则要移动126;...假如在最后一个位置上插入,则移动0;所以平均要移动的次数:(0+127)/2 = 63.5

 

 

 

 

 .

posted @   哟吼--小文文公主  阅读(652)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示