动态链表 1

使用动态链表实现线性表

首先我们先想一个问题!!!
顺序存储结构的不足?
顺序存储结构时,最大的缺点就是插入和删除时需要移动大量的元素,这是很耗费时间的。那么为了解决这个问题我们提出了使用链式存储结构来实现线性表。

1 那么什么是链式存储结构?

链式存储结构就是我们可以用任意一组的存储单元,来存储数据元素,这组存储单元可以是连续的,也可是不连续的。这就意味着,这些数据元素可以存在于内存中任意未被占用的地方。
我们通常使用节点来进行存储数据。

2 什么是节点 ?

每个节点里包括:数据域和指针域。
数据域用来存放元素的数据信息,指针域用来存放指向下一个节点的存储位置。
在这里插入图片描述因此由n个节点,链接成的一个链表即为线性表的存储结构。我们把链表中第一个节点称为头节点 (head);最后一个节点称为尾节点
在这里插入图片描述

3 线性表的链式存储结构的代码实现

实现分析 我们在实现这个类时,定义的方法名为:LinkedList 实现 List接口,重写 List 接口的方法。我们设计一个虚拟的头节点,不存储任何元素,再设计两个指针,一个为头指针(head),一个为尾指针(rear)。
在这里插入图片描述
** 线性表链式存储结构的定义**

public class LinkedList<E> implements List<E>

定义内部类:
设计:因为我们要用节点来存储元素,还要存储指向下一个节点的存储位置,所以我们定义了一个内部类,因为这个类是在内部使用的,所以就直接定义为private

private class Node{

内部类的成员函数和构造方法:
设计:节点里包含了数据域,和指针数据域,所以需要定义出来。创建了一个默认无参的构造函数,和一个携带指定元素的构造方法。

		private E data;    
		private Node next; 
		public Node() {
			this(null, null);
		}
		public Node(E data, Node next) {
			this.data = data;
			this.next = next;
		}

定义成员变量:
设计:我们需要一个头指针和一个尾指针,来方便我们去进行操作,定义一个size记录所有元素的个数。

    private Node head;
	private Node rear;
	private int size;

定义构造函数:
设计:定义一个默认无参的构造函数;

public LinkedList(){
		head = new Node();
		rear = head;
		size = 0;
	}

实现方法:

获取当前线性表的元素个数。

public int getSize() {
		
		return size;
	}

判断当前线性表是否为空。

设计:
什么时候线性表为空,我们设计的头节点是一个虚拟的头节点,没有存储任何的元素,所以当size ==0 时 且 头节点的下一个是空的话,则该线性表为空。

public boolean isEmpty() {
		return size == 0 && head.next == null;
	}

往线性表中指定坐标处,添加元素e
设计:
我们实现插入元素时,先判断要插入元素到指定位置的角标是否越界,然后判断如果当前线性表为空时,head 和 rear 都指向的时 头节点,使用头插法之后,尾指针(rear)后移;这是一种特殊的情况,还有一种线性表为空时,进行尾插法时,尾指针也要后移,这和头插法的处理方式是一样的,我们可以都使用头插法来处理这种问题,然后不是头插,也不是尾插的时候;就执行其他插的方法;执行其他插的方法时我们需要找到要插的节点的前一个节点,在进行插入操作。

public void add(int index, E e) {
		if(index < 0 || index >size){
			throw new IllegalArgumentException("插入角标非法");
		}
		Node n = new Node(e,null);
		if(index == 0) {//头插
			/*Node n = new Node();
			n.data = e;
			n.next = null;*/
			
			n.next = head.next;
			head.next = n;
			if( size == 0){
				rear = n;
			}
			//size++;
			
		} else if (index == size) {
			/*Node n = new Node();
			n.data = e;
			n.next = null;*/
			
			rear.next = n;
			rear = rear.next;
			//size++;
		} else {
			Node p = head;
			for(int i = 0; i < index; i++) {
				p = p.next;
			}
			n.next = p.next;
			p.next = n;
			
		}
		size++;
		
	}

** 获取指定角标的元素 :**
设计:因为我们设计的头节点是不存储数据元素的,获取第一个元素,即就是头节点的下一个节点的数据元素。获取最后一个元素,即就是获取尾节点的数据元素。获取其他节点的元素,从头节点开始,找到要获取元素的节点,返回该节点的元素。

public E get(int index) {
		if(index < 0 || index >=size){
			throw new IllegalArgumentException("角标非法");
		}
		if (index == 0){
			return head.next.data;
		} else if (index == size - 1){
			return rear.data; 
		} else {
			Node p = head;
			for (int i = 0; i <= index; i++){
				p = p.next;
			}
			return p.data;
		}
	}

修改指定角标的元素:

public void set(int index, E e) {
		if(index < 0 || index >=size){
			throw new IllegalArgumentException("修改角标非法");
		}
		if (index == 0){
			head.next.data = e;
		} else if (index == size - 1){
			rear.data = e; 
		} else {
			Node p = head;
			for (int i = 0; i <= index; i++){
				p = p.next;
			}
			p.data = e;
		}
		
	}

删除指定角标的节点 并返回该节点的数据元素 :
设计:
我们根据角标判断,如果为第一个节点时,则进行头删法,如果为最后一个节点,则进行尾删法。为其他时进行删除时,先找到要删除的节点的前一个节点,在进行删除;删除时先获取删除节点的元素,最后再返回删除节点的元素。

public E remove(int index) {
		if(index < 0 || index >=size){
			throw new IllegalArgumentException("插入角标非法");
		}
		E res = null;
		if(index == 0) {
			Node p = head.next;
			res = p.data;
			head.next = p.next;
			p.next = null;
			p = null;
			if(size == 1){
				rear = head;
			}
			/*size--;
			return res;*/
		} else if(index == size-1) {
			Node p = head;
			res = p.data;
			while(p.next != rear) {
				p = p.next;
			}
			p.next = null;
			rear = p;
			/*size--;
			return res;*/
		} else {
			Node p = head;
			for(int i = 0; i < index; i++){
				p = p.next;
			}
			
			Node del = p.next;
			res = del.data;
			p.next = del.next;
			del.next = null;
			/*size--;
			return res;*/
		}
		size--;
		return res;
	}

判断元素e是否存在于当前线性表中
如果存在当前的线性表中,则返回当前元素的角标,如果不存在,则返回值为-1;在实现这个问题时,我们不知道元素e的角标,不能使用for 循环来进行,我们只能使用while循环来执行,判断什么时侯循环结束,有两种情况 :1 是找到了当前的元素 e ,并返回当前元素的角标,2 是要么没找到元素e 直到线性表循环完毕,遍历当前线性表中的节点,知道节点的下一个为空时,循环结束。

public int find(E e) {
		int index = -1;
		if(isEmpty()) {
			return index;
		}
		Node p = head;
		while(p.next != null) {
			p = p.next;
			index++;
			if(p.data == e){
				return index;
			}
		}
		return -1;
	}

LinkedList 的toString 方法:
设计:使用拼接的方法来实现toSring方法。

public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("LinkedList size :"+ getSize() +"\n");
		if(isEmpty()) {
			sb.append("[]");
		} else {
			sb.append("[");
			Node p = head;
			while(p.next != null){
				p = p.next;
				if(p == rear){
					sb.append(p.data + "]");
				} else {
					sb.append(p.data + ",");
				}
			}
		}
		return sb.toString();
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值