Java数据结构和算法(一)--顺序表、链表、静态链表

来源:互联网 发布:域名交易流程 编辑:程序博客网 时间:2024/05/17 07:01

数据结构与算法是程序设计的两大基础,大型的IT企业面试时也会出数据结构和算法的题目。

数据结构是了解计算机存储数据的形式,帮助我们更好的理解计算机存储数据,有人也许会说,我们不是学了数组,直接用数组存储数据不就行了吗?数组存储数据当然是可以的,但是有时候不同的问题我们都用数组反而对我们解决问题不利。

我们首先来看一下顺序表—顺序表是用数组实现的(其中涉及到了泛型的知识,如果不熟悉可以先大概了解之后再看本博客),代码如下:

public class ArrayList<E> {    private Object[] data=null;//数组存放想要保存的数据    private int current;//表示数组中当前为第几个元素    private int capacity;//表示数组的容量    public ArrayList(){        this(10);//默认的线性表的大小    }    public ArrayList(int initialSize){        if(initialSize<0){            throw new  RuntimeException("容量错误:"+initialSize);        }else{            data=new Object[initialSize];            current=0;            capacity=initialSize;        }    }    //检查输入的下标是否有错    public void validIndex(int index){        if(index<0||index>current){            throw new  RuntimeException("下标错误:"+index);        }    }    public void ensureCapacity(){        if(current==capacity){            this.capacity+=10;            Object[] temp=new Object[this.capacity];            for(int i=0;i<current;i++){                temp[i]=data[i];            }        }    }    //添加元素到顺序表中    public void add(E e){        ensureCapacity();        data[current]=e;        current++;    }    //获取下标为index的元素    @SuppressWarnings("unchecked")    public E get(int index){        validIndex(index);        return (E) this.data[index];    }    public int size(){        return current;    }    //将下标为Index的数组元素改为e    public void set(E e,int index){        validIndex(index);        data[index]=e;    }    //将元素e插入到下标为index的数组中,那么可能会移动数组中的元素    public void insert(E e,int index){        validIndex(index);        Object[] temp=new Object[current+1];        for(int i=0;i<index;i++){            temp[i]=data[i];        }        temp[index]=e;        for(int i=index+1;i<current+1;i++){            temp[i]=data[i-1];        }        data=temp;        //将data引用指向temp所指向的地址        //Java数组其实是一个引用类型,使用new关键字将在堆空间创建一个对象        current++;    }    //删除Index的元素,那么可能会移动数组中元素    public void remove(int index){        validIndex(index);        Object[] temp=new Object[current-1];        for(int i=0;i<index;i++){            temp[i]=data[i];        }        for(int i=index;i<current-1;i++){            temp[i]=data[i+1];        }        current--;    }}

现在我们实例化对象并测试一下

public static void main(String[] args) {        ArrayList<String> arraylist=new ArrayList<String>();        String a="hello";        arraylist.add(a);        System.out.println("顺序表的大小为:"+arraylist.size());        String b="word!";        arraylist.insert(b, 1);        System.out.println("顺序表的大小为:"+arraylist.size());        arraylist.display();    }

结果如下:

顺序表的大小为:1顺序表的大小为:2helloword!

当然了, 还有很多方法也可以自己编写测试,这里我只是给大家提供一个思路。

我们发现顺序表的大小需要连续的开辟空间,并且插入、删除中间的元素不太方便,为此我们提出了链表(这个是指单链表)。

这里写图片描述

我们先简单介绍一下链表,图中箭头所指的数据域和指针域合起来我们称之为一个节点(Node),数据域存放数据(根据实际需要存放,如基本数据类型、对象),指针域为引用,指向下一个节点,head为头结点,一般不存放数据,也可以存放链表的长度、数据类型等公共信息。图中最后一个节点的指针域为null表示链表结束。同样head节点的指针域为null表示为空链表.

代码如下:

//自己实现的链式存储结构public class LinkedList<E>{    //头节点不存放数据,也不计入size    private Node<E> head=null;    private int size=0;    public LinkedList(){        if(head==null){            head=new Node<E>();        }    }    public int size(){        return size;    }    public void add(E e){        Node<E> node1=new Node<E>(e);        Node<E> node2=getLastNode();        node2.next=node1;        size++;    }    public Node<E> get(int index){        if(index<0||index>=size){            throw new RuntimeException("下标错误:"+index);        }        int i=0;        Node<E> node=head;        while(i<=index){            node=node.next;            i++;        }        return node;    }    public void insert(int index,E e){        if(index<0||index>size){            throw new RuntimeException("下标错误:"+index);        }else{            Node<E> node=get(index);            Node<E> newNode=new Node<>(e);            newNode.next=node.next;            node.next=newNode;            size++;        }    }    public Node<E> getLastNode(){        Node<E> node=head;        while(node.next!=null){            node=node.next;        }        return node;    }    class Node<e>{        private E e;//节点中存放数据的位置        Node<E> next;//引用指向下一个节点        public Node(){            next=null;        }        public Node(E e){            this.e=e;            next=null;        }        public E getE(){            return e;        }    }}

实例化对象:

public static void main(String[] args) {        String a="hello";        String b="word!";        LinkedList<String> linkedlist=new LinkedList<String>();        linkedlist.add(a);        linkedlist.add(b);        linkedlist.display();}

输出如下:

hello word! 

最后我们来简单介绍一下静态链表。

静态链表其实是为了给没有指针的高级语言设计的一种实现单链表能力的方法。

静态链表是用数组描述的链表,数组的元素都是由两个数据域组成,data和cur。数组的每个下标都对应一个data和一个cur,数据域data用来存放元素,而cur相当于单链表中的next指针,存放该元素的后继在数组中的下标,cur即游标。

这里写图片描述

这两幅图来自《大话数据结构》,描述很详细

这里写图片描述

当然了,对于学习Java的数据结构和算法,我推荐《Java数据结构和算法》(作者为Robert Lafore),当然《大话数据结构》也推荐,只是如果我们时间有限,特别是对于从C语言转换到面对对象的Java,前者更适合而已,如果你时间足够,两者都阅读肯定是有好处的。

代码如下:

/** * 静态链表的最后一个元素的cur指向第一个元素的下标,下标为0表示链表为空 * 静态链表的第一个元素的cur指向第一个备用链表的下标(即是第一个空闲地址的下标) */public class StaticLinkList<E> {    private Node<E>[] data;    public StaticLinkList(){        this(100);    }    @SuppressWarnings("unchecked")    public StaticLinkList(int capacity){        if(capacity<0){            throw new RuntimeException("容量错误:"+capacity);        }else{            data=new Node[capacity];            initialList();        }    }    public void initialList(){    /*最开始建立静态链表的时候,除去最后一个元素的cur,其他全部指向数组的下一个元素的下标,    而最后一个元素指向第一个元素的下标*/        int i=0;        for(;i<data.length-1;i++){            data[i]=new Node<E>(null,i+1);        }        data[i]=new Node<E>(null,0);    }    //添加元素之后,最后一个节点的cur=0    public void add(E e){        if(data[data.length-1].cur==0){            //这时候链表为空,没有任何数据            data[data.length-1].cur=1;            data[1].value=e;            data[1].cur=0;            data[0].cur=2;        }else{            int i=data[data.length-1].cur;            //当一个元素的i退出循环时,i为链表的最后一个元素的下标            while(data[i].cur!=0){                i=data[i].cur;            }            int newcur=data[0].cur;//newcur指向第一个可用的元素下标            int newcur2=data[newcur].cur;//newcur2指向第一个可用下标            data[i].cur=newcur;            data[newcur].cur=0;            data[newcur].value=e;            data[0].cur=newcur2;        }    }    public E get(int index){        //最后一个元素和第一个元素不可用        if(index==0||index==data.length){            return null;        }        return data[index].value;    }    public void display(){        int i=(data[data.length-1]).cur;        while(i!=0){            System.out.print(data[i].value+" ");            i=data[i].cur;        }    }    public void delete(int index){        int i=data.length-1;        while(index!=data[i].cur && i!=0){            i=data[i].cur;        }        if(i==0){            System.out.println("不存在你输入的下标这个元素!!!");        }else{            //进入这个语句块是当i为index的前一个元素的下标            data[i].cur=data[index].cur;            //直接跳过你想删除的下标所在的元素            data[index].value=null;            data[index].cur=data[0].cur;            data[0].cur=index;        }    }    //节点类    @SuppressWarnings("hiding")    class Node<E>{        private E value;        private int cur;        public Node(E value,int cur){            this.value=value;            this.cur=cur;        }    }}

测试代码如下:

public static void main(String[] args) {        StaticLinkList<Integer> staticlinklist=new StaticLinkList<Integer>();        for(int i=0;i<10;i++){            staticlinklist.add(i);        }        staticlinklist.display();        System.out.println();        staticlinklist.delete(6);//删除数字5        staticlinklist.display();    }

输出结果如下:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 6 7 8 9 

今天就给大家分享这么多了,谢谢阅览。

原创粉丝点击