线性表链式存储结构——单链表

线性表链式存储结构

  • 线性表的链式存储结构,与顺序结构中的数组不同的是,不需要开辟连续的内存,链式,主要是内存中一些比较离散的数据通过存储相互之间的地址,来联系到一起。每个数据元素除了存储自己的数据以外,还需要存储后继元素的地址。**注意:这里说的是后继。因为有可能是双向链表。**下图就是一个链表。前面也说过,链式比顺序好的一点是,删除元素,添加元素相对于顺序比较容易。
    在这里插入图片描述

单链表

  • 单向链表,只有一个方向的链表,每个节点存放的地址只有一个它的后继元素的地址。比如:下图就是一个单项链表。每个节点都存有他本身的元素(data),以及的的后继元素的地址(next).比较容易理解。
    在这里插入图片描述
  1. 所以现在已经明白什么是链式存储结构,什么是单链表了,然后之后要考虑的是,要用代码怎么实现这个单链表。
  2. 既然链表每一个节点都是独立的,可以建立一个节点,让这个节点包含data域和指针域,所以可以想到定义一个类,首先明白一点,我们实现的是链表,因此用户不需要明白底层源码是如何实现的,所以这个节点类(Node)是私有的,而且我们有必须使用这个节点类,所以我们可以想到内部类。
     private class Node {//内部类Node
		E data;// 数据域
		Node next;// 指针域
		public Node() {//构造方法初始话节点为空
			this(null, null);
		}
		public Node(E data, Node next) {
			this.data = data;
			this.next = next;
		}
		@Override//重写的toString()方法
		public String toString() {
			return data.toString();
		}
	}
  1. 节点也建立好了,之后要考虑的是如何将这些数据连接到一起,按照最简单的思维,比如有一个新元素,我们要加到链表中,之后陆续来了一些新元素,我们要接入,但是我们应该想到链表从何时开始,又从何是结束,这是我们应该设置两个标记,也就是头指针,尾指针来标记一下,头尾指针就是用来记录头节点和尾结点的。存储头尾结点的地址。
    在这里插入图片描述
    这里有一点需要注意,尾指针没有意外指的是链表尾元素。但是头指针却不同。有两种情况需要注意一下。就是虚拟头节点和真实头结点。
    虚拟头节点:也被称为头节点,但是越没有元素,只是标记一下头指针的位置,当中的数据域没有存储任何元素值得注意在学校时,也有数据结构的课程,不过是C语言数据结构,中间也有链表中的,头结点的课程,在其中,建立一个链表是,用的最多的就是虚拟头节点,从下一个节点开始存元素,这才是实际意义上的节点。与C语言数据结构上的一样,在单链表中我们还是用虚拟的多一点。
    在这里插入图片描述
    真实头节点:有实际意义的头节点,与每个节点都一样,有数据,也都下一节点的地址。
    在这里插入图片描述4. 到此,头尾结点,指针都已经设立好了,之后就是给链表里面加元素了。在链表中,有两种最基本的添加元素的方法头插法尾插法下面是单链表中最重要的几个方法!
    头插法:每次都在首节点之前添加元素,每次添加元素之后新元素都为首节点。注意:这里我说的是首元素,是为了区分头节点,因为使用的是虚拟头节点,所以我这里所说的首节点是头节点的下一个节点。
    在这里插入图片描述
  //头插法核心代码
   Node n = new Node(e, null);//将要插入的新元素
   if (index == 0) { // 头插
			n.next = head.next;//先将头结点的下一个元素的地址给新结点
			head.next = n;//将n的地址给头结点的next
			if (size == 0) {
				rear = n;
			}

尾插法:
在这里插入图片描述

if (index == size) { // 尾插
			rear.next = n;//将n的地址给尾指针的next
			rear = n;//尾结点后移,指向新的节点n
		}

一般插入
在这里插入图片描述

        Node p = head;//申请一个游标指针,以便于找待插入的位置
        //从头节点依次往后移直到待插入位置的前一个节点
			for (int i = 0; i < index; i++) {
				p = p.next;//指针往后移
			}
			n.next = p.next;将待插位置的后一个节点的地址给n,
			p.next = n;//最后将新结点的地址给p的下一跳,就完成了一般插入操作

当然我们也可以将头插尾插,以及一般插入结合起来。
在这里插入图片描述 5. 然后还有get(int index)功能,就是获取指定位置的元素。获取指定角标数据时,首先要做的就是判断指定角标是否越界,之后,因为实现的是链表,所以没有角标,因此只能通过遍历来实现查找元素的功能。目前还没有办法优化。

public E get(int index) {
		//判断指定位置角标是否越界。
		if (index < 0 || index >= size) {
		//如果越界,抛出角标越界异常
			throw new IllegalArgumentException("插入角标非法");
		}
		if (index == 0) {
		//获取首元素
			return head.next.data;
		} else {
			Node p = head;
			for (int i = 0; i <= index; i++) {
				p = p.next;
			}
			return p.data;
		}
	}
  1. 之后重要的几个功能还有删除功能。
    在这里插入图片描述
    这时候要注意的是,要对删除位置进项判断
    (1). 如果角标越界是不可以进行删除的。
        if (index < 0 || index >= size) {
			throw new IllegalArgumentException("");
		}

(2). 删除的链表中只有一个节点。
在这里插入图片描述
(3). 删除的是首节点。
在这里插入图片描述

       if (index == 0) {// 头删
			Node p = head.next;//先申请一个节点用来指向将要删除的头节点。
			res = p.data;//用于返回的值
			head.next = p.next;//将删除的节点的后继结点的地址给头节点
			p.next = null;//因为此时p还指向它的后继节点,所以置空。
			p = null;
			if (size == 1) {//如果此时只有一个节点,尾指针要指向头节点。
				rear = head;
			}
			size--;//删除一个节点后,总长度要减一。
			return res;
		}

(4). 删除的是尾结点。
在这里插入图片描述
删除尾结点是比较简单的。只需要将尾指针的前一个节点的指针域置空,尾指针往前移就好了。当然,要找尾结点的前一个节点,必须循环遍历。

if (index == size - 1) {//尾删
			res = rear.data;//用来接要删除的节点的值。
			Node p = head;//游标节点,用来找尾结点的前一个结点。
			while (p.next != rear) {
				p = p.next;
			}
			p.next = null;//倒数第二个节点置空
			rear = p;//尾指针前移
			size--;//重要:长度减一
			return res;
		}

(5). 删除的节点位置在链表中间(一般删除)。
在这里插入图片描述
删除这种不特殊位置的节点时,应该先找这个待删除的节点,之后在进行删除,找节点是还是需要使用循环,此时用for循环比较合适。

       //一般删除
            Node p = head;
			for (int i = 0; i < index; i++) {//用for循环寻找待删除元素的前驱节点
				p = p.next;
			}
			Node del = p.next;
			res = del.data;
			p.next = p.next.next;//待删节点的后继节点的地址赋值给带删节点的前驱节点
			size--;//长度减一
			del.next = null;//这也可以有,也可以没有,将待删节点的指针域置空,此时已完成删除
			return res;

这就是建立单链表的大致过程。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值