数据结构— 循环链表、双向链表【插入&删除】、双向循环链表

简介: 数据结构— 循环链表、双向链表【插入&删除】、双向循环链表

第二章:线性表

 (一) 循环链表

       1.定义

              循环链表也称为环形链表,其结构与单链表相似,只是将单链表的首尾相连。将最后一个结点的后继指针指向第一个结点。

               非空循环列表

e9b11abc0d6441cf90643897bdb3f8b2.png  空循环列表

5719d054d9774ea7b059c6cb260f8e0e.png

// p结点是尾结点
p.next = head;

2.算法:循环链表合并

分析:分析:分析:

bd412cc7355343858fbf296878d23adb.png

核心算法

/* 变量
    a尾: taila
    a头:taila.next
    b尾:tailb
    b头:tailb.next
*/
// 先将b尾指向a的头,需要定义p记录b尾原来的值
// 再将a的尾指向b的头
Node p = tailb.next;            //记录b尾的指向,也就是b头
tailb.next = taila.next;        //b尾指向a头
taila.next = p.next;            //a尾执行b头

(二) 双向链表


        1.定义


               一个结点由两个指针域,一个指针域指向前驱结点,另一个指针域指向后继结点,这种链表称为双向链表


                       数据域 :data


                       前驱结点指针域:prior


                       后继结点指针域:next

608f769f356f4402820f9c59bd35bdbb.png


2. 双向链表的结点类

package data.linear_table.node;
//双向链表的结点类
public class DuLNode {
    public Object data ;    //存放结点值的数据域
    public DuLNode prior ;  //存放指向前驱结点的指针域
    public DuLNode next ;  //存放指向后继结点的指针域
    //无参时的构造函数
    public DuLNode() {
        this(null);
    }
    //构造数据域值为data的新结点
    public DuLNode(Object data){
        this.data = data ;
        this.prior = null ;
        this.next = null ;
    }
}

3.算法:插入

               需求:向结点p前面插入新结点s

87af4068d41b47fbb1ef3789886d18f8.png

算法核心

/* 变量
  结点a:p.prior
*/
p.prior.next = s;   //结点a指向结点s
s.prior = p.prior;    // 结点s指向结点a
p.prior = s;      //结点p指向结点s
s.next = p;       //结点s执行结点p
//注意:必须先处理结点a,否则无法获得结点a

3. 算法: 删除

               需求:删除p结点

e86779c00f4d4b13bfca1011dfc5d4b5.png

核心算法

// a.next = b
p.prior.next = p.next;
// b.prior = a
p.next.prior = p.prior

(三) 双向循环链表

       双向链表与单链表一样,只要首尾相连,即可构成双向循环链表


0aa377871ac84ad49d0fa609bcf3c3b0.png

线性表接口

package data.linear_table;
//线性表接口
public interface IList {
    public void clear() ;  //清空
    public boolean isEmpty();  //判断是否为空
    public int length();   // 表的长度
    public Object get(int i) throws Exception; //获取元素的值
    public void insert(int i , Object x) throws Exception; //在指定位置,插入指定元素
    public void remove(int i ) throws Exception; //删除指定元素
    public int indexOf(Object x) ;  //查找指定元素第一次出现的位置
    public void display() ;  //输出元素的值
}

双向链表的结点类

package data.linear_table.node;
//双向链表的结点类
public class DuLNode {
    public Object data ;    //存放结点值的数据域
    public DuLNode prior ;  //存放指向前驱结点的指针域
    public DuLNode next ;  //存放指向后继结点的指针域
    //无参时的构造函数
    public DuLNode() {
        this(null);
    }
    //构造数据域值为data的新结点
    public DuLNode(Object data){
        this.data = data ;
        this.prior = null ;
        this.next = null ;
    }
}

双向循环链表类

package data.linear_table.linked_list;
import data.linear_table.IList;
import data.linear_table.node.DuLNode;
import java.util.Scanner;
//双向链表类
public class DuLinkList implements IList {
    public DuLNode head ;    //双向循环链表的头结点
    //双向链表的构造函数,构造只含1个头结点的双向循环链表
    public DuLinkList () {
        head = new DuLNode();   //初始化头结点
        head.prior = head ;     //初始化结点的前驱和后继
        head.next = head ;
    }
    //从表尾到表头逆向创建双向循环链表,其中n为链表的结点个数
    //尾插法
    public DuLinkList(int n) throws Exception {
        this() ;
        Scanner sc = new Scanner(System.in);    //构造用于输入的对象
        for (int j = 0 ; j < n ; j++ ) {
            insert(0,sc.next());        //生成新结点,插入到表头
        }
    }
    //插入结点
    @Override
    public void insert(int i, Object x) throws Exception {
        DuLNode p = head.next;      //初始化, p 指向首结点, j为计数器
        int j = 0 ;
        while (!p.equals(head) && j < i ) {     //寻找插入位置
            p = p.next ;    //指向后继结点
            j ++ ;          // 计数器值增1
        }
        if (j != i && !p.equals(head)) {
            throw new Exception("位置不合法");  //抛出异常
        }
        DuLNode s = new DuLNode(x);         //生成新结点s
        p.prior.next = s ;                  //将新结点s插入到第i个结点的p前面
        s.prior = p.prior ;
        s.next = p ;
        p.prior = s ;
    }
    //删除结点
    @Override
    public void remove(int i) throws Exception {
        DuLNode p = head.next;      //初始化, p 指向首结点, j为计数器
        int j = 0 ;
        while (!p.equals(head) && j < i ) {     //寻找插入位置
            p = p.next ;    //指向后继结点
            j ++ ;          // 计数器值增1
        }
        if (j != i ) {
            throw new Exception("删除位置不合理");  //抛出异常
        }
        //修改指针,使第i个结点p从链表中脱离出来
        p.prior.next = p.next ;
        p.next.prior = p.prior ;
    }
    //输出
    @Override
    public void display() {
        DuLNode node = head.next;       //取出带头结点的双向循环链表的首结点
        while (!node.equals(head)) {
            System.out.print(node.data +" ");     //输出结点的数据域值
            node = node.next ;
        }
        System.out.println();   //换行
    }
}

(四) 每日一练


17. 线性表是有 n 个( )的有限序列。C


A.数据表


B.字符


C.数据元素


D.数据项


-------------------------------------------------------


18. 线性表是一个( )。A


A.有限序列,可以为空


B.有限序列,不可以为空


C.无限序列,可以为空


D.无限序列,不可以为空


-------------------------------------------------------


19. 以下( )是一个线性表。B


A.由 n 个实数组成的集合


B.由 100 个字符组成的序列


C.由所有整数组成的序列


D.所有奇数组成的序列


-------------------------------------------------------


20. 在线性表中,除了开始元素外,每个元素( )。A


A.只有唯一的前驱元素


B.只有唯一的后即元素字符


C.有多个前驱元素


D.有多个后继元素


-------------------------------------------------------


21. 顺序表的最大有优点是( )。A


A.存储密度大  存储密度是1


B.插入运算方便


C.删除运算方便


D.可以方便地用于各种逻辑的存储表示   顺序存储


-------------------------------------------------------


22. 对于顺序表,访问编号为 i 的元素的时间复杂度为( )。B


A. O(n)


B. O(1)


C.O(nlog2n)


D.O(log2n)


-------------------------------------------------------


23. 对于顺序表,在编号为 i 处插入一个新元素的间复杂度为( )。A


A. O(n)


B. O(1)


C.O(nlog2n)


D.O(log2n)


-------------------------------------------------------


24. 采用顺序查找法对长度为 n 的线性表进行查找(不采用表尾设监视哨的方法),最坏的


情况下要进行( )次元素间的比较。B


A.n+2


B.n


C.n-1


D.n/2


-------------------------------------------------------


25. 带头结点的单向链表的头指针为 head,该链表为空的判定条件是( )的值为真。C


A.head = = NULL


B.head.getNext()= =head


C.head.getNext()= = NULL


D.head = =head.getNext()


-------------------------------------------------------


26. 非空的单向循环链表的尾结点满足( )(设头指针为 head,指针 p 指向尾结点)。C


A.p.getNext()= =NULL


B.p= =NULL


C.p.getNext()= =head


D.p= =head


-------------------------------------------------------


27. 链表所具备的特点是( )。D


A.可以随机访问任一结点


B.占用连续的存储空间


C.可以通过下标对链表进行直接访问


D.插入删除元素的操作不需要移动元素结点


-------------------------------------------------------


28. 设链表中的结点是 Node 类型的类,且有 Node p;为了申请一个新结点,并由 p 指向该


结点,可用以下 Java 语句( )。 A


A. p=new Node();


B. p=new Node(*);


C.p=(NODE )malloc(sizeof(p));


D.p=(NODE *)malloc(sizeof(p));


-------------------------------------------------------


29. 设顺序存储的线性表长度为 n,对于插入操作,设插入位置是等概率的,则插入一个元


素平均移动元素的次数为( )。A P34


A.n/2


B.n


C.n-1


D.n-i+1


-------------------------------------------------------


30. 设顺序存储的线性表长度为 n,对于删除操作,设删除位置是等概率的,则删除一个元


素平均移动元素的次数为( )。A  P35


A.(n-1)/2


B.n


C.2n


D.n-i


-------------------------------------------------------


31. 设顺序存储的线性表长度为 n,要删除第 i(0<=i<=n-1)个元素,按课本的算法,当 i=


( )时,移动元素的次数为 3。 C


A.3 (第n位置,下标为n-1,移动0步)


B.n/2 (第n-1位置,下标为n-2,移动1步)


C.n-4  (第n-2位置,下标为n-3,移动2步)


D.4 (第n-3位置,下标为n-4,移动3步)


-------------------------------------------------------


32. 设顺序存储的线性长度为 n,要在第 i(0<=i<=n)个元素之前插入一个新元素,按课本


的算法当 i= ( )时,移动元素次数为 2。 D


A.n/2 (n-1元素的前面插入新元素,表示n元素位置为空,且需要移动1次。)


B.n (n-2元素的前面插入新元素,表示n和n-1都是空,且需要移动2次)


C.1


D.n-2


-------------------------------------------------------


33. 设有一个长度为 n 的顺序表,要删除第 i(0<=i<=n-1)个元素,按照课本算法,需移动


元素的个数为( )。C


A.n-i+1 i=n-1 -->0


B.n-i i=n-2 -->1


C.n-i-1 i=0 -->n-1


D.i


-------------------------------------------------------


34. 下述各线性结构中可以随机访问的是( )。 D


A. 单向链表


B. 双向链表


C. 单向循环链表


D. 顺序表


35. 线性表采用链式存储时,其地址( )。C


A.一定是不连续的


B.必须是连续的


C.可以连续也可以不连续


D.部分地址必须是连续的


-------------------------------------------------------


36. 在一个单链表中,p、q 分别指向表中两个相邻的结点,且 q 所指结点是 p 所指结点的


直接后继,现要删除 q 所指结点,可用的语句是( )。C

f30e0eb7332143488e64905809562b58.png


A.p=q.getNext();


B.p.setNext(q);


C.p.setNext(q.getNext());        p.next = q.next;


D.q.setNext(NULL);


-------------------------------------------------------


37. 在一个单链表中 p 所指结点之后插入一个 s 所指的结点时,可执行( )。D


f97650d1b0834dc7865e7a55ee585a14.png


A.p.setNext(s); s.setNext(p.getNext());


B.p,setNext(s.getNext());


C.p=s.getNext();


D.s.setNext(p.getNext()); p.setNext(s); s.next = p.next;     p.next = s;


-------------------------------------------------------


38. 按照教材算法,在一个长度为 n 的顺序表中为了删除位序号为 5 的元素,从前到后依次


移动了 15 个元素。则原顺序表的长度为( )。A


A. 21  6 + 15 = 21


B. 20


C. 19


D. 25


-------------------------------------------------------


39. 针对线性表,在存储后如果最常用的操作是取第 i 个结点及其前驱,则采用( )存


储方式最节省时间。C


A.单链表 读取的时间复杂度O(n)


B.双链表   读取的时间复杂度O(n)


C.顺序表   读取的时间复杂度O(1)


D.单循环链表 读取的时间复杂度O(n)


-------------------------------------------------------


40. 假设在顺序表中,每一个数据元素所占的存储单元的数目为 4,且第一个数据元素的存


储地址为 100,则位序号是 7 的数据元素的存储地址是:( )。D


A.106


B.107


C.124


D.128 100 + 4*7 = 128


相关文章
|
16天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
44 4
|
17天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
17天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
16天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
37 0
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
5天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
14 1
|
8天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
11天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
13天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
40 4
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
30 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器