通过Java实现单链表来透彻理解抽象数据类型的定义和应用

原创 2013年12月02日 10:52:11

  Java实现单链表  

       本文将数据结构知识中重要知识点数据元素,结点,数据类型,抽象数据类型,抽象数据类型的实现、以及对新定义的数据结构的应用等知识通过下述java代码的形式串联起来以从宏观上对数据结构有一个透彻的理解和认识。

       我们要使用单链表这个数据结构来解决问题的前提是首先得创建一个单链表数据结构。创建单链表数据结构,就得自定义个单链表的抽象数据类型,抽象数据类型只是对数据结构定义一组逻辑操作,没有具体的实现。在实际应用中,必须实现这个抽象数据类型,才能使用它们,而实现抽象数据类型依赖于数据存储结构。

1. 为单链表的数据元素自定义结点类 Node.java

/**
 *为单链表自定义的结点类
 *单链表结点类,泛型参数T指定结点的元素类型
 */
public class Node<T> 
{
    public T data;   //数据域,保存数据元素
    public Node<T> next; //地址域,引用后继结点

    public Node(T data, Node<T> next)//构造结点,data指定数据元素,next指定后继结点
    {
        this.data = data;
        this.next = next;
    }
    public Node()//无参构造函数
    {
        this(null, null);
    }

    //4、Node类可声明以下方法:
    public String toString()                     //返回结点元素值对应的字符串
    {
        return this.data.toString();
    }    
    public boolean equals(Object obj)            //比较两个结点值是否相等,覆盖Object类的equals(obj)方法
    {
        return obj==this || obj instanceof Node && this.data.equals(((Node<T>)obj).data);
    }    
}
2.使用Java接口为线性表自定义的抽象数据类型   :LList.java

/**
 * 为数据结构线性表自定义的抽象数据类型
 * 在Java中,抽象数据类可以使用接口来描述
 * 线性表接口LList,描泛型参数T表示数据元素的数据类型
 */
public interface LList<T>     //线性表接口,泛型参数T表示线性表中的数据元素的数据类型
{
    boolean isEmpty();        //判断线性表是否空
    int length();             //返回线性表长度
    T get(int i);             //返回第i(i≥0)个元素
    void set(int i, T x);     //设置第i个元素值为x
    void insert(int i, T x);  //插入x作为第i个元素
    void append(T x);         //在线性表最后插入x元素
    T remove(int i);          //删除第i个元素并返回被删除对象
    void removeAll();         //删除线性表所有元素
    T search(T key);          //查找,返回首次出现的关键字为key元素
    String toString();        //返回显示线性表所有元素值对应的字符串
}

3.实现线性表接口---抽象数据类型的实现类-LinkedList.java(链表类,提供Llist接口中抽象方法的具体实现)

/**
 *线性表的链式表示和实现
 *带头结点的单链表类
 *实现线性表接口
 */
public class LinkedList<T>implements LList<T>//带头结点的单链表类,实现线性表接口
{
	protected Node<T> head;//头指针,指向单链表的头结点
	//默认构造方法,构造空单链表。创建头结点,data和next值均为null
	public LinkedList()
	{
		this.head=new Node<T>();
	}
	由指定数组中的多个对象构造单链表,采用尾插入构造单链表
	public LinkedList(T[] element){
		
		this(); //创建空单链表,只有头结点
		Node<T> rear = this.head;//rear指向单链表的最后一个结点
		/*
		 *若element==null,抛出空对象异常
		 *element.length==0时,构造空链表 
		 */
		for(int i=0;i<element.length;i++){
			rear.next=new Node<T>(element[i],null);//尾插入,创建结点链入rear结点之后
			rear=rear.next;//rear指向新的链尾结点
		}
	}
	//判断单链表是否为空,O(1)
	public boolean isEmpty(){
		return this.head.next==null;
	}
	//求链表的长度
	public int length(){
		int i=0;
		Node<T> p = this.head.next;//p从单链表第一个结点开始
		while(p!=null){ //若单链表未结束
			i++;
			p=p.next;//p到达后继结点
		}
		return i;
	}
	//返回单链表所有元素的描述字符串,形式为“(,)”,覆盖Object类的toString()方法,O(n)
	public String toString(){
		String str="(";
		Node<T> p =this.head.next;
		while(p!=null){
			str+=p.data.toString();
			if(p.next!=null){
				str +=",";	//不是最后一个结点时后加分隔符		
			}
			p=p.next;
		}
		return str+=")";
	}
	//返回第i(i>=0)个元素,若i无效,则返回null
	public T get(int i){
		if(i>=0){
			Node<T> p=this.head.next;
			for(int j=0; p!=null&&j<i;j++)
				p=p.next;
			if(p!=null)
				return p.data;//p指向第i个结点
		}
		return null;
	}
	//设置第i(i>=0)个元素值为x,若i指定序号无效则抛出序号越界异常
	public void set(int i,T x){
		if(x==null)
			return;//不能设置空对象
		if(i>=0){
			Node<T> p =this.head.next;
			for(int j =0;p!=null&&j<i;j++){
				p=p.next;
			}
			if(p!=null)
				p.data=x;
		}else throw new IndexOutOfBoundsException(i+"");//抛出序号越界异常
	}
	//将x对象出插入在序号为i结点前,O(n)
	public void insert(int i,T x){
		if(x==null)
			return;
		Node<T> p =this.head;
		for(int j =0;p.next!=null&&j<i;j++){
			p=p.next;
		}
		p.next=new Node<T>(x,p.next);		
	}
	//在单链表的最后添加对象,O(n)
	public void append(T x){
		insert(Integer.MAX_VALUE,x);
	}
	//删除序号为i的结点
	public T remove(int i){
		if(i>0){
			Node<T> p=this.head;
			for(int j =0;p.next!=null&&j<i;j++)
				p=p.next;
			if(p.next!=null){
				T old=p.next.data;
				p.next=p.next.next;
				return old;
			}
		}
		return null;
	}	
	//删除链表所有元素,Java自动收回各结点所占用的内存空间
	public void removeAll(){
		this.head.next=null;
	}
/*
	public T search(T key){
		if(key==null)
			return null;
		Node<T> p=this.head.next;
		while(p!=null&&p.data.compareTo(key)<=0){
			if(p.data.compareTo(key)==0)
				return p.data;
			p=p.next;
			}
		return null;		
	}
*/
	//查找,返回首次出现的关键字为key的元素
    public T search(T key) 
    {
        if (key==null)
            return null;
        for (Node<T> p=this.head.next;  p!=null;  p=p.next)
            if (p.data.equals(key))
                return p.data;
        return null;
    }
}

4.利用前面新创建的单链表数据类型,实现单链表逆转 SinglyLinkedList_reverse.java

注:LinkedList<T>声明为泛型类,类型形式参数T表示链表元素的数据类型。当声明LinkedList类的对象并创建实例时,再指定泛型参数T的实际类型参数为一个确定,例如:LinkedList<String> list = new LinkedList<String>();  LinkedList<Integer> list = new LinkedList<Integer>();   这样可保证一个链表中的所有数据元素是相同类及其子类的对象。如果向链表添加指定泛型以外的对象,则会出现编译错误。T 的实际类型参数必须是类,不能使int 、char等基本数据类型。如果需要表示基本数据类型,则必须使用对应数据类型的包装类,如Integer 、Character等。

/**
 *利用前面新创建的单链表数据类型,实现单链表逆转
 */
public class SinglyLinkedList_reverse 
{
    //将单链表逆转,泛型方法,返回值类型前声明类型参数T
    //public static <T> void reverse(SinglyLinkedList<T> list)
    public static <T> void reverse(LinkedList<T> list)
    {
        Node<T> p=list.head.next, succ=null, front=null;   //head必须声明为public 
        while (p!=null)
        {
            succ = p.next;  //设置succ是p结点的后继结点
            p.next = front; //使p.next指向p结点的前驱结点
            front = p;
            p = succ;       //p向后走一步
        }
        list.head.next = front;
    }
  
    public static void main(String args[])
    {
        String value[]={"A","B","C","D","E","F"};
        //SinglyLinkedList<String> list = new SinglyLinkedList<String>(value);
        LinkedList<String> list = new LinkedList<String>(value);
        System.out.println("list: "+list.toString());
        reverse(list);
        System.out.println("逆转后 "+list.toString());
    }
}
/*
程序运行结果如下:    
list: (A, B, C, D, E, F)
逆转后 (F, E, D, C, B, A)
*/
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

抽象数据类型定义(ADT)

一、抽象数据类型定义(ADT) 作用:抽象数据类型可以使我们更容易描述现实世界。例:用线性表描述学生成绩表,用树或图描述遗传关系。 定义:一个数学模型以及定义在该模型上的一组操作。 关键:使...

JAVA中关于链表的操作和基本算法

import java.util.HashMap; import java.util.Scanner; import java.util.Stack; /** * * @author Thin...

Java 链表的定义与使用

Java实现链表主要依靠引用传递,引用可以理解为地址,链表的遍历多使用递归,这里我存在一个疑问同一个类的不同对象的的相同方法的方法内调用算不算递归.这里我写的是单向链表;package com.exa...

Java个人理解之链表的使用

链表的精髓就在于递归。废话不多说,直接上代码。PS:这里只有增加数据以及输出数据的功能 class Manage{ private Node root; //这里我将根节点放在管理类中,我们一切的操作...

抽象数据类型(ADT) 双链表实现

/*main.c----测试函数*/ #include #include #include"list.h" static void show(const ITEM * item) { prin...

数据结构 严蔚敏 清华大学出版社 第二章 抽象数据类型 链表的实现 成功编译并运行

/* main.cpp * 对各项功能的检验//function.cpp 函数的定义 // Created by Cooper on 11/10/2016. // #include "linklis...

抽象数据类型(链表)

ADT.h ADT.c main.c
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)