数据的存储一般分线性存储结构和链式存储结构两种。前者是一种顺序的存储方式,在内存中用一块连续的内存空间存储数据,即逻辑上相连的物理位置相邻,比较常见的就是数组;后者是一种链式存储方式,不保证顺序性,逻辑上相邻的元素之间用指针所指定,它不是用一块连续的内存存储,逻辑上相连的物理位置不一定相邻。本篇主要介绍链式存储结构基于链表的实现,使用的语言为Java。
链表是一种递归的数据结构,它要么为空(null),要么指向是指向一个结点(node)的引用,该节点含有一个泛型元素(该泛型元素可以是任意数据类型),和一个指向另一个链表的引用。链表有很多种,下面主要介绍单向链表、双端链表、有序链表、双向链表。
单向链表:单向链表是最简单、最基础的链表,它的一个结点(node)分两部分,第一部分存储结点的数据信息(data),第二部分存储指向下一结点的地址(next)信息。最后一个结点(链尾)指向一个空地址(null)。单向链表一般只在链表表头(链头)结点的位置插入元素,这样每次新加入的元素都会在链头位置,而最先加入的元素会在链尾位置。删除操作时,如果在链头位置删除,只需要把头结点指向其下一个结点即可;如果是在中间位置删除,只需要将其前一个结点指向其下一个结点即可。单向链表示意图如下图所示:
单向链表的Java代码实现:
importjava.util.Stack;public classLinkedListOnePoint {private Node head; //头结点
private int size; //链表长度,即链表中结点数量
publicLinkedListOnePoint(){
head= null;
size= 0;
}//私有内部类,代表链表每个结点
private classNode{private Object data; //链表结点的值
private Node next; //指向的下一个结点
publicNode(Object data){this.data =data;
}
}//判断链表是否为空
public booleanisEmpty(){return size==0?true:false;
}//返回链表长度
public intsize(){returnsize;
}//查看链表头结点,不移除
publicObject headNode(){if(size == 0) return null;returnhead.data;
}//在链表表头插入一个结点(入栈)
public voidinsertInHead(Object obj){
Node newNode= newNode(obj);if(size == 0){
head=newNode;
}else{
newNode.next=head;
head=newNode;
}
size++;
}//删除链表表头结点(出栈)
publicObject deleteHeadNode(){if(size == 0) return null;
Object obj=head.data;if(head.next == null){
head= null; //只有一个结点
}else{
head=head.next;
}
size--;returnobj;
}//链表查找:判断链表中是否包含某个元素
public booleancontainObject(Object obj){if(size == 0) return false;
Node n=head;while(n != null){if(n.data == obj) return true;else n =n.next;
}return false;
}//删除链表中的指定结点(如果包含多个指定结点,只会删除第一个)
public booleandeleteNode(Object obj){if(size == 0){
System.out.println("链表为空!");return false;
}//先在链表中查询是否包含该结点,找到之后获取该结点和其前一个结点
Node previous = null; //前一个结点
Node current = head; //当前结点
while(current.data !=obj){if(current.next == null){
System.out.println("没有找到该结点!");return false;
}
previous=current;
current=current.next;
}if(current ==head){this.deleteHeadNode();
}else{
previous.next=current.next;
size--;
}return true;
}//正向打印链表
public voiddisplay(){if(size == 0) System.out.println("链表为空!");
Node n=head;while(n != null){
System.out.print("
n=n.next;
}
System.out.println();
}//反向打印链表(用栈)
public voidprintListFromTailToHead(Node node){if(node == null) System.out.println("链表为空!");
Stack sta = new Stack();while(node != null){
sta.push((Integer) node.data);//先将链表压入栈中
node =node.next;
}while(!sta.empty()){
System.out.print(sta.pop()+"
}
System.out.println();
}//反向打印链表(递归)
public voidprintListFromTailToHeadDiGui(Node node){if(node == null){
System.out.println("链表为空!");
}else{if(node.next != null){
printListFromTailToHeadDiGui(node.next);
}
System.out.print(node.data+"
}
}public static voidmain(String[] args) {
LinkedListOnePoint list= newLinkedListOnePoint();
System.out.println(list.isEmpty());//true
System.out.println(list.size()); //0
list.display(); //链表为空!
list.printListFromTailToHead(list.head); //链表为空!
list.insertInHead(0);
list.insertInHead(1);
list.insertInHead(2);
list.insertInHead(3);
list.display();//
list.printListFromTailToHead(list.head); //0
list.printListFromTailToHeadDiGui(list.head); //0
System.out.println(list.isEmpty()); //false
System.out.println(list.size()); //4
System.out.println(list.containObject(1)); //true
}
}
View Code
我们知道,栈是一种“后进先出”的数据结构,对栈的插入和删除操作都是在栈头位置进行的,这与在单向链表的表头插入和删除元素的原理类似,因此可以用单向链表实现栈。
单向链表实现栈的Java代码:
/** 单链表实现栈*/
public classLinkedListToStack {privateLinkedListOnePoint linkedlist;publicLinkedListToStack(){
linkedlist= newLinkedListOnePoint();
}//栈大小
public intsize(){returnlinkedlist.size();
}//是否为空栈
public booleanisEmpty(){returnlinkedlist.isEmpty();
}//入栈
public voidpush(Object obj){
linkedlist.insertInHead(obj);
}//出栈
publicObject pop(){if(this.isEmpty()) return null;returnlinkedlist.deleteHeadNode();
}//查看栈顶元素
publicObject peek(){if(this.isEmpty()) return null;returnlinkedlist.headNode();
}//打印栈中元素
public voiddisplay(){
linkedlist.display();
}public static voidmain(String[] args) {
LinkedListToStack stack= newLinkedListToStack();
stack.push(0);
stack.push(1);
stack.push(2);
stack.push(3);
stack.display();//
System.out.println(stack.peek()); //3
System.out.println(stack.pop()); //3
System.out.println(stack.pop()); //2
System.out.println(stack.pop()); //1
System.out.println(stack.pop()); //0
System.out.println(stack.pop()); //null
}
}
View Code
双端链表:双端链表和单向链表大体上是一样的,不同的是,单向链表在表尾部分插入元素时,需要从头结点一直遍历到尾结点才能进行插入操作,这样难免有些繁琐。因此如果加入一个对尾结点的引用,这样就可以很方便地在尾结点进行插入操作,这就是双端链表。除了有一个头结点(head),还有一个尾结点(tail)。注意它和后面双向链表的区别!
双端链表的Java代码实现:
importjava.util.Stack;/** 双端链表,比单链表多了个尾结点*/
public classLinkedListTwoPoint {private Node head; //头结点
private Node tail; //尾结点
private int size; //链表长度,即链表中结点数量
publicLinkedListTwoPoint(){
head= null; //头结点
tail = null; //尾结点
size = 0; //链表长度,即链表中结点数量
}//私有内部类,代表链表每个结点
private classNode{private Object data; //链表结点的值
private Node next; //指向的下一个结点
publicNode(Object data){this.data =data;
}
}//判断链表是否为空
public booleanisEmpty(){return size==0?true:false;
}//返回链表长度
public intsize(){returnsize;
}//查看链表头结点,不移除
publicObject headNode(){if(size == 0) return null;returnhead.data;
}//查看链表尾结点,不移除
publicObject tailNode(){if(size == 0) return null;returntail.data;
}//在链表表头插入一个结点
public voidinsertInHead(Object obj){
Node newNode= newNode(obj);if(size == 0){
head=newNode;
tail=newNode;
}else{
newNode.next=head;
head=newNode;
}
size++;
}//在链表表尾插入一个结点
public voidinsertInTail(Object obj){
Node newNode= newNode(obj);if(size == 0){
head=newNode;
tail=newNode;
}else{
tail.next=newNode;
tail=newNode;
}
size++;
}//删除链表表头结点
publicObject deleteHeadNode(){if(size == 0) return null;
Object obj=head.data;if(head.next == null){ //只有一个结点
head = null;
tail= null;
}else{
head=head.next;
}
size--;returnobj;
}//链表查找:判断链表中是否包含某个元素
public booleancontainObject(Object obj){if(size == 0) return false;
Node n=head;while(n != null){if(n.data == obj) return true;else n =n.next;
}return false;
}//删除链表中的指定结点(如果包含多个指定结点,只会删除第一个)
public booleandeleteNode(Object obj){if(size == 0){
System.out.println("链表为空!");return false;
}//先在链表中查询是否包含该结点,找到之后获取该结点和其前一个结点
Node previous = null; //前一个结点
Node current = head; //当前结点
while(current.data !=obj){if(current.next == null){
System.out.println("没有找到该结点!");return false;
}
previous=current;
current=current.next;
}if(current ==head){this.deleteHeadNode();
}else{
previous.next=current.next;
size--;
}return true;
}//正向打印链表
public voiddisplay(){if(size == 0) System.out.println("链表为空!");
Node n=head;while(n != null){
System.out.print(n.data+ "
n=n.next;
}
System.out.println();
}//反向打印链表(用栈)
public voidprintListFromTailToHead(Node node){if(node == null) System.out.println("链表为空!");
Stack sta = new Stack();while(node != null){
sta.push((Integer) node.data);//先将链表压入栈中
node =node.next;
}while(!sta.empty()){
System.out.print(sta.pop()+"
}
System.out.println();
}//反向打印链表(递归)
public voidprintListFromTailToHeadDiGui(Node node){if(node == null){
System.out.println("链表为空!");
}else{if(node.next != null){
printListFromTailToHeadDiGui(node.next);
}
System.out.print(node.data+"
}
}public static voidmain(String[] args) {
LinkedListTwoPoint list= newLinkedListTwoPoint();
System.out.println(list.isEmpty());//true
System.out.println(list.size()); //0
list.display(); //链表为空!
list.printListFromTailToHead(list.head); //链表为空!
list.insertInHead(0);
list.insertInHead(1);
list.insertInHead(2);
list.insertInHead(3);
list.display();//3
list.printListFromTailToHead(list.head); //0
list.printListFromTailToHeadDiGui(list.head); //0
System.out.println(list.isEmpty()); //false
System.out.println(list.size()); //4
System.out.println(list.containObject(1)); //true
}
}
View Code
我们知道,队列是一种“先进先出”的数据结构,队列的插入操作是在队尾进行的,而删除操作是在队头进行的,这与在双端链表的表尾插入和在表头删除操作是类似的,因此可以用双端链表实现队列。
双端链表实现队列的Java代码:
/** 双端链表实现队列*/
public classLinkedListToQueue {privateLinkedListTwoPoint linkedlist;publicLinkedListToQueue(){
linkedlist= newLinkedListTwoPoint();
}//队列大小
public intsize(){returnlinkedlist.size();
}//是否为空队列
public booleanisEmpty(){returnlinkedlist.isEmpty();
}//入列,在链表表尾插入节点
public voidadd(Object obj){
linkedlist.insertInTail(obj);
}//出列,在链表表头删除结点
publicObject poll(){if(this.isEmpty()) return null;returnlinkedlist.deleteHeadNode();
}//查看队列头元素
publicObject peekHead(){if(this.isEmpty()) return null;returnlinkedlist.headNode();
}//查看队列尾元素
publicObject peekTail(){if(this.isEmpty()) return null;returnlinkedlist.tailNode();
}//打印队列元素
public voiddisplay(){
linkedlist.display();
}public static voidmain(String[] args) {
LinkedListToQueue stack= newLinkedListToQueue();
stack.add(0);
stack.add(1);
stack.add(2);
stack.add(3);
stack.display();//0
System.out.println(stack.peekHead()); //0
System.out.println(stack.peekTail()); //3
System.out.println(stack.poll()); //0
System.out.println(stack.poll()); //1
System.out.println(stack.poll()); //2
System.out.println(stack.poll()); //3
System.out.println(stack.poll()); //null
}
}
View Code
有序链表:链表本身是一种无序的数据结构,元素的插入和删除不能保证顺序性,但是有没有有序的链表呢?答案是肯定的,我们在单向链表中插入元素时,只需要将插入的元素与头结点及其后面的结点比较,从而找到合适的位置插入即可。一般在大多数需要使用有序数组的场合也可以使用有序链表,有序链表在插入时因为不需要移动元素,因此插入速度比数组快很多,另外链表可以扩展到全部有效的使用内存,而数组只能局限于一个固定的大小中。
有序链表的Java代码实现:
/** 有序链表*/
public classLinkedListInOrder {private Node head; //头结点
private int size; //链表长度,即链表中结点数量
publicLinkedListInOrder(){
head= null;
size= 0;
}//私有内部类,代表链表每个结点
private classNode{private Integer data; //链表结点的值
private Node next; //指向的下一个结点
publicNode(Integer data){this.data =data;
}
}//判断链表是否为空
public booleanisEmpty(){return size==0?true:false;
}//返回链表长度
public intsize(){returnsize;
}//在链表中插入一个结点,保持链表有序性(头结点最小,尾结点最大)
public voidinsertNode(Integer obj){
Node newNode= newNode(obj);if(size == 0){ //空链表直接放入头结点
head =newNode;
}else{
Node previous= null; //插入位置前一个结点
Node current = head; //插入位置后一个结点(当前结点)
while(current.data
previous =current;
current=current.next;
}if(current.next == null){ //如果插入的结点大于链表中所有结点,则放到链尾
current.next =newNode;
}else{
previous.next= newNode; //在合适位置插入
newNode.next =current;
}
}
size++;
}//删除链表表头结点
publicObject deleteHeadNode(){if(size == 0) return null;
Object obj=head.data;if(head.next == null){
head= null; //只有一个结点
}else{
head=head.next;
}
size--;returnobj;
}//正向打印链表
public voiddisplay(){if(size == 0) System.out.println("链表为空!");
Node n=head;while(n != null){
System.out.print("
n=n.next;
}
System.out.println();
}public static voidmain(String[] args) {
LinkedListInOrder list= newLinkedListInOrder();
System.out.println(list.isEmpty());//true
System.out.println(list.size()); //0
list.display(); //链表为空!
list.insertNode(0);
list.insertNode(1);
list.insertNode(2);
list.insertNode(3);
list.insertNode(2);
list.insertNode(4);
list.display();//
System.out.println(list.isEmpty()); //false
System.out.println(list.size()); //6
System.out.println("*****************************");
}
}
View Code
双向链表:前面的几种链表只能从头结点遍历到尾结点这一个方向,每个结点都只能指向其下一个结点。而双向链表的每个结点既能指向下一个结点,又能指向前一个结点,双向链表既能从头结点向尾结点遍历,又能从尾结点向头结点遍历,既有一个头结点,又有一个尾结点。
双向链表的Java代码实现:
/** 双向链表
* 单向链表只可向一个方向遍历,一般查找一个结点的时候需要从第一个结点开始每次访问下一个结点,一直访问到需要的位置。
* 双向链表的每个结点都有指向的前一个结点和后一个节点,既有链表表头又有表尾,即可从链头向链尾遍历,又可从链尾向链头遍历。
* LinkedList中的私有静态内部类Node实际上就是一个双向链表,代表泛型,指明结点的数据类型
* private static class Node {
E item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}*/
public classLinkedListTwoDirections {private Node head; //头结点
private Node tail; //尾结点
private int size; //链表长度,即链表中结点数量
publicLinkedListTwoDirections(){
head= null;
tail= null;
size= 0;
}//私有内部类,代表链表每个结点
private classNode{private Object data; //链表结点的值
private Node previous; //当前结点指向的前一个结点
private Node next; //当前结点指向的下一个结点
publicNode(Object data){this.data =data;
}
}//判断链表是否为空
public booleanisEmpty(){return size==0?true:false;
}//返回链表长度
public intsize(){returnsize;
}//查看链表头结点,不移除
publicObject headNode(){if(size == 0) return null;returnhead.data;
}//查看链表尾结点,不移除
publicObject tailNode(){if(size == 0) return null;returntail.data;
}//在链表表头插入一个结点
public voidinsertInHead(Object obj){
Node newNode= newNode(obj);if(size == 0){
head=newNode;
tail=newNode;
}else{
newNode.next=head;
head.previous=newNode;
head=newNode;
}
size++;
}//在链表表尾插入一个结点
public voidinsertInTail(Object obj){
Node newNode= newNode(obj);if(size == 0){
head=newNode;
tail=newNode;
}else{
newNode.previous=tail;
tail.next=newNode;
tail=newNode;
}
size++;
}//删除链表表头结点
publicObject deleteHeadNode(){if(size == 0) return null;
Object obj=head.data;if(head.next == null){ //只有一个结点
head = null;
tail= null;
}else{
head=head.next;
head.previous= null;
}
size--;returnobj;
}//删除链表表尾结点
publicObject deleteTailNode(){if(size == 0) return null;
Object obj=tail.data;if(tail.previous == null){ //只有一个结点
head = null;
tail= null;
}else{
tail=tail.previous;
tail.next= null;
}
size--;returnobj;
}//正向打印链表
public voiddisplay(){if(size == 0) System.out.println("链表为空!");
Node n=head;while(n != null){
System.out.print("
n=n.next;
}
System.out.println();
}public static voidmain(String[] args) {
LinkedListTwoDirections list= newLinkedListTwoDirections();
System.out.println(list.isEmpty());//true
System.out.println(list.size()); //0
list.display(); //链表为空!
list.insertInHead(0);
list.insertInHead(1);
list.insertInHead(2);
list.insertInHead(3);
list.display();//
System.out.println(list.deleteHeadNode()); //3
list.insertInTail(1);
list.insertInTail(2);
list.display();//
}
}
View Code
转载请注明出处 http://www.cnblogs.com/Y-oung/p/8886142.html
工作、学习、交流或有任何疑问,请联系邮箱:yy1340128046@163.com 微信:yy1340128046