您的位置:首页 > 理论基础 > 数据结构算法

数据结构之链表的使用与实现

2015-08-27 10:12 465 查看

一、链表

   链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

  

   使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。

二、链表的使用(java)

  ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。

  LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率低。

1、ArrayList

  ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处:动态的增加和减少元素、实现了ICollection和IList接口、灵活的设置数组的大小。ArrayList是以数组方式实现的链表,是非同步的,在多线程的环境下使用需要同步。

  常用方法见:百度百科

  

一个简单地使用例子:  

ArrayList<Integer> list=new ArrayList<>();
for( int i=0;i<10;i++ ) //给数组增加10个Int元素
list.add(i);
list.remove(5);//将第6个元素移除
for( int i=0;i<3;i++ ) //再增加3个元素
list.add(i+20);
String str=list.toString();
System.out.println(list);


2、LinkedList

  LinkedList的使用方法和ArrayList相似,知识实现方法不同,ArrayList是采用数组的方式,而LinkedList是采用链表的方式实现的如果集合元素在生成之后变化不大,使用ArrayList,如果数据经常发生变化,应该使用LinkedList。

三、链表的实现(java)

1、单链表

  单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。

public class Node {
protected Node next; //指针域
protected int data;//数据域

public Node( int data) {
this. data = data;
}
}
public class LinkList {
public Node first; // 定义一个头结点
private int pos = 0;// 节点的位置

public LinkList() {
this. first = null;
}

// 插入一个头节点
public void addFirstNode( int data) {
Node node = new Node(data);
node. next = first;
first = node;
}

// 删除一个头结点,并返回头结点
public Node deleteFirstNode() {
Node tempNode = first;
first = tempNode. next;
return tempNode;
}

// 在任意位置插入节点 在index的后面插入
public void add(int index, int data) {
Node node = new Node(data);
Node current = first;
Node previous = first;
while ( pos != index) {
previous = current;
current = current. next;
pos++;
}
node. next = current;
previous. next = node;
pos = 0;
}

// 删除任意位置的节点
public Node deleteByPos( int index) {
Node current = first;
Node previous = first;
while ( pos != index) {
pos++;
previous = current;
current = current. next;
}
if(current == first) {
first = first. next;
} else {
pos = 0;
previous. next = current. next;
}
return current;
}

// 根据节点的data删除节点(仅仅删除第一个)
public Node deleteByData( int data) {
Node current = first;
Node previous = first; //记住上一个节点
while (current. data != data) {
if (current. next == null) {
return null;
}
previous = current;
current = current. next;
}
if(current == first) {
first = first. next;
} else {
previous. next = current. next;
}
return current;
}
}


2、双向链表

  双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。

public class DoublyLinkList {
private class Node{
private Object obj;
private Node left = null;
private Node right = null;

Node(Object obj){
this.obj = obj;
}
}

private Node first = null;
private Node last = null;

public void insertFirst(Object obj){
Node node = new Node(obj);
if(first == null){
last = node;
}else{
node.right = first;
first.left = node;
}
first = node;
}

public void insertLast(Object obj){
Node node = new Node(obj);
if(first == null){
first = node;
}else{
last.right = node;
node.left = last;
}
last = node;
}

public boolean insertAfter(Object target,Object obj){
Node node = new Node(obj);
Node cur = first;
while(cur != null){
if(cur.obj.equals(target)){
node.right = cur.right;
node.left = cur;
if(cur == last)
last = node;
else
cur.right.left = node;
cur.right = node;
return true;
}
cur = cur.right;
}
return false;
}

public Object deleteFirst() throws Exception{
if(first == null)
throw new Exception("empty!");
Node temp = first;
if(first.right == null){
first = null;
last = null;
}else{
first.right.left = null;
first = first.right;
}
return temp;
}

public Object deleteLast() throws Exception{
if(first == null)
throw new Exception("empty!");
Node temp = last;
if(first.right == null){
first = null;
last = null;
}else{
last.left.right = null;
last = last.left;
}
return temp;
}

public Object delete(Object obj) throws Exception{
if(first == null)
throw new Exception("empty!");
Node cur = first;
while(cur != null){
if(cur.obj.equals(obj)){
if(cur == last)
last = cur.left;
else
cur.right.left = cur.left;
if(cur == first)
first = cur.right;
else
cur.left.right = cur.right;
return obj;
}
cur = cur.right;
}
return null;
}
}


3、单向循环链表

  循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。 

单向循环链表和单链表实现的区别:

1.)添加一个结点到单向循环链表末尾时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为null。

2.)判断是否到达表尾时,单向循环链表可以判断该结点是否指向头结点,单链表只需要知道是否为null。

// 结点类,包含结点的数据和指向下一个节点的引用
public class Node<E> {
private E data; // 数据域
private Node<E> next; // 指针域保存着下一节点的引用

public Node(E data) {
this.data = data;
}

public Node(E data, Node<E> next) {
this.data = data;
this.next = next;
}

public E getData() {
return data;
}

public void setData(E data) {
this.data = data;
}

public Node<E> getNext() {
return next;
}

public void setNext(Node<E> next) {
this.next = next;
}
}

public class CircularLinkedList<E> {
private Node<E> tail; // 尾结点
private int size; // 链表长度

public CircularLinkedList() {
tail = null;
size = 0;
}

// 在头结点前插入
public boolean addBeforeHead(E data){
Node<E> newNode = new Node<E>(data);
if(isEmpty()){
tail = newNode;
tail.setNext(newNode); // 尾结点指向头结点
newNode.setNext(tail); // 头结点指向尾结点
}else{
Node<E> head = tail.getNext();
tail.setNext(newNode);
newNode.setNext(head);
}
size++;
return true;
}

// 在尾结点后插入
public boolean addAfterTail(E data){
Node<E> newNode = new Node<E>(data);
if(isEmpty()){
tail = newNode;
tail.setNext(newNode);
newNode.setNext(tail);
}else{
Node<E> head = tail.getNext(); // 获取头结点
tail.setNext(newNode); // 将原尾结点指向新结点
tail = newNode; // 将新节点设置为尾结点
newNode.setNext(head); // 将新尾结点指向头结点
}
size++;
return true;
}

// 在某位置上插入(position为结点位置,不是角标)
public boolean insert(int position,E data){
if(position >= 1 && (position <= size + 1)){
if(isEmpty() || position == 1){ // 在头结点前插入
addBeforeHead(data);
}else if(position == size + 1){ // 在尾结点后插入
addAfterTail(data);
}else{ // 在中间位置插入
Node<E> preNode = get(position - 1); // 获取position的前一结点
Node<E> originalNode = preNode.getNext(); // 获取未插入结点时position位置对应结点
Node<E> newNode = new Node<E>(data);
preNode.setNext(newNode);
newNode.setNext(originalNode);
size++;
return true;
}
}
return false;
}

// 删除对应位置的结点
public E delete(int position){
E result = null;
if(position >= 1 && position <= size){
if(position == 1){ // 删除头结点
result = tail.getNext().getData();
Node<E> afterHead = tail.getNext().getNext();
tail.setNext(afterHead);
}else if(position == size){ // 删除尾结点
result = tail.getData();
Node<E> preTail = get(position - 1);
preTail.setNext(tail.getNext());
tail = preTail;
size--;
}else{ // 删除其他结点
Node<E> preNode = get(position - 1);
Node<E> curNode = preNode.getNext();
result = curNode.getData();
preNode.setNext(curNode.getNext());
size--;
}
}
return result;
}

// 获取某个位置的结点
public Node<E> get(int position){
Node<E> targetNode = null;
if(!isEmpty() && position >= 1 && position <= size){
targetNode = tail.getNext(); // 获取头结点
for(int i = 1; i < position ; i++){
targetNode = targetNode.getNext(); // 循环获取对应位置的结点
}
}
return targetNode;
}

// 获取链表的长度
public int getSize(){
return size;
}

// 判断链表是否为空
public boolean isEmpty(){
return size == 0;
}

// 打印链表中的数据
public void display(){
Node<E> node = tail.getNext();  // 获取头结点
System.out.print("单向循环链表: ");
for(int i = 0; i < size; i++){
System.out.print(" " + node.getData());
node = node.getNext();
}
System.out.println("");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: