上一篇文章介绍了线性表中的顺序表,今天介绍线性表的链表。
链式存储结构的特点
顺序存储结构,是在内存中分配一组地址连续的地址来存储线性表中的元素,需要一次性分配连续的一片空间,链表采用一组地址任意的存储单元存放线性表中的数据元素,所以需要在每个数据元素中保存一个引用下一个元素的引用。
对比线性表的两种实现
hk | 顺序表 | 链表 |
空间 | 需要在内存中静态分配一组地址连续的内存,数组长度固定,所以总有一些数组元素被浪费 | 存储地址动态分布,空间不会被浪费,但需要一些空间来保存下一个或上一个结点的指针,所以也要牺牲一部分空间 |
时间 | 顺序表中的逻辑顺序与物理存储顺序相同,所以查找,读取性能很好,插入删除时,需要移动结点之后的元素,数组扩容时,需要复制数组元素,所以插入删除性能较差。 | 链表采用链式结构保存表内的元素,插入删除性能很好,查找性能不如顺序表 |
1.单链表的简单实现:
package xxx.hk.linearlist;
public class LinkList<T> {
// 链表的长度
private int size = 0;
// 链表的头部
private Node header;
// 链表的尾部
private Node tail;
// 链表结点
private class Node {
// 结点的值
private T data;
// 下一个结点
private Node next;
public Node(T data, Node next) {
super();
this.data = data;
this.next = next;
}
}
public LinkList() {
header = null;
tail = null;
}
public LinkList(T data) {
header = new Node(data, null);
tail = header;
size = 1;
}
/**
* 根据索引值返回结点
*
* @param index
* @return 该索引的结点
*/
public Node getNodeByIndex(int index) {
if (index < 0 || index > size - 1) {
throw new IndexOutOfBoundsException("下标越界异常");
}
Node currentNode = header;
for (int i = 0; i < size && header != null; i++, currentNode = currentNode.next) {
if (i == index) {
return currentNode;
}
}
return null;
}
/**
* 根据索引值返回结点的值
*
* @param index
* @return 结点的值
*/
public T get(int index) {
return getNodeByIndex(index).data;
}
/**
* 尾插法插入结点
*
* @param data
*/
public void add(T data) {
Node node = new Node(data, null);
// 如果链表为空
if (header == null) {
header = node;
tail = header;
} else {
tail.next = node;
tail = node;
}
size++;
}
/**
* 头插法插入结点
*
* @param data
*/
public void addHead(T data) {
Node node = new Node(data, null);
// 如果链表为空
if (tail == null) {
header = node;
tail = header;
} else {
node.next = header;
header = node;
}
size++;
}
/**
* 插入到索引处
* @param data
* @param index
*/
public void insert(T data, int index) {
// 插入位置小于0 插入到第一个
if (index <= 0) {
addHead(data);
// 插入位置大于链表长度或者链表为空,插入到最后一个
} else if (index >= size) {
add(data);
} else {
//得到该索引前一个结点
Node preNode = getNodeByIndex(index-1);
//修改next所指的结点
preNode.next = new Node(data, preNode.next);
size++;
}
}
/**
* 删除索引处的结点
* @param index
* @return 删除结点的值
*/
public T deleteByIndex(int index){
if(index<0||index>size){
throw new IndexOutOfBoundsException("下标越界异常");
}
Node delNode = null;
//如果删除头结点
if(index==0){
delNode = header;
header = header.next;
}else{
Node preNode = getNodeByIndex(index-1);
delNode = preNode.next;
preNode.next = delNode.next;
}
size--;
return delNode.data;
}
/**
* 删除最后一个
* @return
*/
public T delete(){
return deleteByIndex(size-1);
}
/**
* 根据值找到索引值
* @param data
* @return 索引值 为找到则为-1
*/
public int getIndex(T data){
if(header==null){
return -1;
}
Node current = header;
for(int i=0;i<size&¤t!=null;i++,current=current.next){
if(current.data.equals(data)){
return i;
}
}
return -1;
}
/**
* 是否为空
* @return true/false
*/
public boolean isEmpty(){
return size==0;
}
/**
* 清空链表
*/
public void clear(){
header = null;
tail = null;
size = 0;
}
/**
* 链表的长度
* @return
*/
public int length(){
return size;
}
/*
* 重写的tostring方法
* @see java.lang.Object#toString()
*/
@Override
public String toString(){
if(isEmpty()){
return "[]";
}else{
StringBuilder sb = new StringBuilder("[");
for(Node current = header;current!=null;current = current.next){
sb.append(current.data+",");
}
int len = sb.length();
return sb.delete(len-1,len).append("]").toString();
}
}
//测试
public static void main(String[] args) {
LinkList<String> list = new LinkList<>();
list.add("hk");
System.out.println(list.length());
System.out.println(list);
list.add("wyh");
list.add("123");
System.out.println(list.length());
System.out.println(list);
System.out.println(list.isEmpty());
list.insert("aa", 1);
System.out.println(list);
list.delete();
System.out.println(list);
list.deleteByIndex(1);
System.out.println(list);
System.out.println(list.getIndex("hk"));
list.clear();
System.out.println(list);
}
}
2.循环链表:尾结点的下一结点指针指向头结点
3.双向链表:每个元素结点包含 上一个结点和下一个结点的引用
package xxx.hk.linearlist;
public class DuLinkList<T> {
// 链表长度
private int length;
// 头结点
private Node header;
// 尾结点
private Node tail;
// 结点内部类
private class Node {
// 结点的值
T data;
// 上一个结点
Node prev;
// 下一个结点
Node next;
public Node(T data, DuLinkList<T>.Node prev, DuLinkList<T>.Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
public DuLinkList() {
header = null;
tail = null;
}
public DuLinkList(T data) {
header = new Node(data, null, null);
tail = header;
length = 1;
}
public DuLinkList(T data1, T data2, Node head, Node tail) {
header = new Node(data1, null, tail);
tail = new Node(data2, header, null);
length = 2;
}
/**
* 尾插法插入结点
*
* @param data
*/
public void add(T data) {
// 链表为空
if (header == null) {
header = new Node(data, null, null);
tail = header;
length++;
} else {
DuLinkList<T>.Node node = new Node(data, tail, null);
tail.next = node;
tail = node;
length++;
}
}
/**
* 头插法插入结点
*
* @param data
*/
public void addAtHead(T data) {
if (header == null) {
header = new Node(data, null, null);
tail = header;
length++;
} else {
DuLinkList<T>.Node node = new Node(data, null, header);
header.prev = node;
header = node;
length++;
}
}
/**
* 根据索引值返回结点
*
* @param index
* @return 索引值对应的结点
*/
public Node get(int index) {
if (index < 0 || index > length - 1) {
throw new IndexOutOfBoundsException("下标越界");
}
// 从链首开始找
if (index <= length / 2) {
Node current = header;
for (int i = 0; i < length && current != null; i++, current = current.next) {
if (index == i) {
return current;
}
}
} else { // 从链尾开始找
Node current = tail;
for (int i = length - 1; i >= 0 && current != null; i--, current = current.prev) {
if (index == i) {
return current;
}
}
}
return null;
}
/**
* 在指定位置插入元素结点
*
* @param data
* @param index
*/
public void addAtIndex(T data, int index) {
// 插入到链首
if (index == 0) {
addAtHead(data);
// 插入到链尾
} else if (index == length) {
add(data);
} else {
Node node = get(index);
Node newNode = new Node(data, node.prev, node);
node.prev = newNode;
length++;
}
}
/**
* 删除指定位置的结点
*
* @param index
* @return 删除结点的值
*/
public T delAtIndex(int index) {
if (index == 0) {
return removeFirst();
} else if (index == length - 1) {
return remove();
} else {
Node node = get(index);
T data = node.data;
node.prev.next = node.next;
node.next.prev = node.prev;
node = null;
length--;
return data;
}
}
/**
* 删除最后一个
*
* @return 删除结点的值
*/
public T remove() {
// 如果链表不为空
if (tail != null) {
T data = tail.data;
tail = tail.prev;
tail.next = null;
length--;
return data;
}
return null;
}
/**
* 删除第一个
*
* @return 删除结点的值
*/
public T removeFirst() {
// 如果链表不为空
if (header != null) {
T data = header.data;
header = header.next;
header.prev = null;
length--;
return data;
}
return null;
}
/**
* 判断链表是否为空
* @return true/false
*/
public boolean isEmpty(){
return length>0?false:true;
}
//返回链表的长度
public int length(){
return length;
}
/*
* 打印链表
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
if(isEmpty()){
return "[]";
}else{
StringBuilder sb = new StringBuilder("[");
Node current = header;
for(int i = 0;i<length&¤t!=null;i++,current=current.next){
sb.append(current.data+",");
}
//删除最后一个,
sb.append("]").delete(sb.length()-2, sb.length()-1);
return sb.toString();
}
}
/**
* 倒叙打印链表
* @return String 链表的所有节点的值s
*/
public String reverseToString(){
if(isEmpty()){
return "[]";
}else{
StringBuilder sb = new StringBuilder("[");
Node current = tail;
for(int i = length;i>0&¤t!=null;i++,current=current.prev){
sb.append(current.data+",");
}
//删除最后一个,
sb.append("]").delete(sb.length()-2, sb.length()-1);
return sb.toString();
}
}
public static void main(String[] args) {
DuLinkList<Integer> duLinkList = new DuLinkList<>();
/*System.out.println(duLinkList.length);
System.out.println(duLinkList.isEmpty());
*/
duLinkList.add(1);
/*System.out.println(duLinkList.length);
System.out.println(duLinkList.isEmpty());*/
duLinkList.add(2);
duLinkList.addAtHead(0);
duLinkList.addAtIndex(-1, 0);
duLinkList.addAtIndex(3,4);
//duLinkList.remove();
//duLinkList.delAtIndex(0);
duLinkList.delAtIndex(4);
System.out.println(duLinkList.toString());
System.out.println(duLinkList.reverseToString());
}
}
顺序表、链表分别对应java中的 ArrayList、LinkedList的实现,可以参考源代码进行模仿改进。