简介:
本文介绍了数据结构中线性表的两种实现方式:线性表和链表
- 顺序表就是在内存中按顺序连续开辟一段空间来存储数据的结构,在java中就是数组,如a所示
- 链表就是在内存中随机开辟内存一段段存储数据的结构,如图b所示
线性表的接口
使用一个接口表明基本操作的需求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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(); }
|
顺序表基本操作代码实现
使用顺序表来实现Ilist接口,重写接口中所有的抽象类方法,注释就能看懂大部分,这里需要注意的是插入和删除两个操作,下面图解:
插入操作
从下图可以看出,想要在i-1出插入x,那么就要将该位置后面所有的元素向后移动,并且应该从an开始移动,再a(n-1),最后把ai从i-1移动到i位置,再插入x
删除操作
删除某个地方的元素后,该位置就会空出来,那么我们只需要把后面的元素依次往前移动补上去就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| public class SqList implements Ilist{ private Object[] listElem; private int curLen;
public SqList(int maxSize) { curLen = 0; listElem = new Object[maxSize]; }
@Override public void clear() { curLen = 0; }
@Override public boolean isEmpty() { return curLen == 0; }
@Override public int length() { return curLen; }
@Override public Object get(int i) throws Exception{ if(i < 0 || i >= curLen){ throw new Exception("索引越界"); } return listElem[i]; }
@Override public void insert (int i, Object x) throws Exception{ if(curLen == listElem.length){ throw new Exception("数组满了,无法插入新元素"); } if(i < 0 || i > curLen){ throw new ArrayIndexOutOfBoundsException("索引越界"); } for (int j = curLen; j > i ; j--) { listElem[j] = listElem[j-1]; } listElem[i] = x; curLen++; }
@Override public void remove(int i) throws Exception{ if(i < 0 || i >= curLen){ throw new Exception("索引错误"); } for (int j = i; j < curLen-1; j++) { listElem[i] = listElem[i+1]; } curLen--; }
@Override public int indexOf(Object x) { for (int i = 0; i < curLen; i++) { if (listElem == x){ return i; } } return -1; }
@Override public void display() { for (int i = 0; i < curLen; i++) { System.out.print(listElem[i]+""); } System.out.println(); } }
|
单链表基本操作代码实现
链表需要先定义一个节点类Node用来存放数据以及指向下一个节点的指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Node { public Object data; Node next;
public Node() { this(null,null); }
public Node(Object data) { this(data,null); }
public Node(Object data, Node next) { this.data = data; this.next = next; } }
|
这里链表的插入和删除操作需要单独进行图解:
插入
先使p指向head节点,往后移动到i-1的位置,第一步使得q.next指向p.next.next,再把p.next连接到q上。注意,这里顺序不能反了,如果反了,链表后半部分就找不到了
删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| import java.util.Scanner;
public class LinkList implements Ilist{ private int curLen; public Node head;
public LinkList() {
curLen = 0; head = new Node(); }
public LinkList(int n, boolean order) throws Exception { this(); System.out.println("请输入"+n+"个字符串,以回车分割"); if(!order){ creatTail(n); } else{ creatHead(n); } }
@Override public void clear() { head.data = null; head.next = null; curLen = 0; }
@Override public boolean isEmpty() { return head.next == null; }
@Override public int length() { return curLen; }
@Override public Object get(int i) throws Exception { if (i < 0 || i >= curLen){ throw new Exception("索引错误"); } Node p = head.next; for (int j = 0; j < i; j++) { p = p.next; } return p.data; }
@Override public void insert(int i, Object x) throws Exception { if (i > curLen){ throw new Exception("索引错误"); } Node p = head; Node q = new Node(x); for (int j = -1; j < i - 1; j++) { p = p.next; } q.next = p.next; p.next = q; curLen++; }
@Override public void remove(int i) throws Exception { if (i < 0 || i >= curLen){ throw new Exception("索引错误"); } Node p = head.next; for (int j = 0; j < i - 1; j++) { p = p.next; } p.next = p.next.next; curLen--; }
@Override public int indexOf(Object x) { Node p = head.next; int j = 0; if (p != null){ if (p.data == x) return j; j++; p = p.next; } return -1; }
@Override public void display() { Node p = head.next; while (p != null){ System.out.print(p.data+" "); p = p.next; } System.out.println(); }
public void creatHead(int n) throws Exception { Scanner sc = new Scanner(System.in); for (int i = 0; i < n; i++) { insert(0,sc.next()); } }
public void creatTail(int n) throws Exception { Scanner sc = new Scanner(System.in); for (int i = 0; i < n; i++) { insert(length(),sc.next()); } }
}
|
链表和数组优缺点比较
数组实现比较简单,查找效率高,插入和删除效率低,需要预分配空间,一旦分配尺寸就固定了
链表比较灵活,插入和删除的效率比较高,可以动态分配空间,但是查找效率低
所以红黑树就结合了两者的特点,使得查找效率十分高效,后面再学习红黑树