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