首页 > 代码库 > 数据结构和算法(Java版)

数据结构和算法(Java版)

主要内容:(内容来自互联网以及李刚著作的《突破程序员基本功的16课》)

         1.数据结构:线性表、栈、队列、树&二叉树

         2.算法:常用内部排序(选择、插入、交换、归并)
 
 
 
目录:
    1. 线性表(顺序存储、链式存储单链表、双向链表)-------------2014/10/15
    2. 栈(顺序栈、链栈)                                        -------------2014/10/15
 
线性表(顺序存储、链式存储):一对一松耦合关系。(List就是线性表的意思)
基本特征(非空、有限的线性表)
1. 首位元素唯一
2. 除最后一个元素外,每个元素只有一个后继元素
3. 除第一个元素外,每个元素只有一个前驱元素
 
基本操作:
1. 初始化(构造器、创建空的线性表)
2. 返回长度(元素个数)
3. 获取指定索引处的元素
4. 查找数据元素的索引 (仅仅返回找到的第一个,没找到则返回-1)
5. 指定位置插入元素(长度+1)
6. 尾部插入元素(长度+1)
7. 删除指定位置的元素(长度-1)
8. 删除尾部的元素(长度-1)
 
 
顺序结构:SequenceList
1. 逻辑地址和物理地址一致(逻辑上连续表现为物理地址上的连续)
2. 地址的计算 :   an=a0+nb;   (b表示每个元素的存储单元大小)
3. 从2的公式看的出访问表中任意一个元素不存在时间上时间上的差异,即顺序结构可以实现随机访问(查找快)
4. 增删比较麻烦(操作决定它要腾挪和变化容量)
------------------------------------------------------------------------------------------------------------------------
链式结构:LinkList
1. 不能随机访问
2. 插入删除效率比较高
 
 
 
 
 
 
顺序结构实现
import java.util.Arrays;public class SequenceList<T> {    private final int DEFAULT_SIZE = 16; //定义一个常量        private int capacity = DEFAULT_SIZE;   //保存数组的容量    private Object[] elementData; //用数组实现    private int size=0; //数组当前元素个数        public SequenceList()  //1.创建一个链表并且初始化    {        elementData = new Object[capacity];    }        public SequenceList(T element)//以一个元素为基础创建链表    {        this();        elementData[0]=element;        size++;    }        public SequenceList(int initSize)  //指定初始容量    {        //最好要先判断一个传进来的参数        //指定的容量是2的n次方        capacity = 1;        while(capacity<initSize)        {            capacity<<=1;        }        elementData = new Object[capacity];    }        public int Length()   //2.获取list的大小    {        return size;    }        public T get(int index)    //3.获取指定索引处的元素    {        //传进来的参数一定要判断其合法性        if(index<0 || index>size-1)        {            throw new IndexOutOfBoundsException("数组下标越界");            }        return (T)elementData[index];    }        public int indexOf(T element) //4.查找指定元素的索引    {        for(int i=0;i<capacity;i++)        {            if(element.equals(elementData[i]))            {                return i;            }        }        return -1;    }        public void insert(T element,int index) //5.在指定位置插入一个元素    {        if(index<0 || index>size)        {            throw new IndexOutOfBoundsException("数组下标越界");            }        //先处理数组长度问题,保证数组长度增加后仍然安全        if((size+1)>capacity)        {            //容量不够了,扩容,把原数组copy到新数组            while(capacity<(size+1))            {                capacity <<=1;            }            elementData = Arrays.copyOf(elementData, capacity); //效率不好        }        //把index处的数组元素后移        System.arraycopy(elementData,index,elementData,index+1,size-index);        elementData[index]=element;        size++;    }    public void insertLast(T element) //6.在尾部新增一个元素    {        insert(element,size);   //注意尾部是size而不是capacity    }    public T remove(int index)  //7.删除指定位置的元素    {        if(index<0 || index>size-1)        {            throw new IndexOutOfBoundsException("线性表索引越界");        }        T oldValue = (T)elementData[index];        int numMoved = size - index - 1; //index后面多少个元素都要前移        if(numMoved>0)        {            System.arraycopy(elementData, index+1, elementData, index, numMoved);        }        elementData[--size] = null;        return oldValue;    }        public T removeLast()  //8.删除尾部的元素    {        return remove(size-1);    }        public boolean empty()   //判断表是否为空    {        return size==0;    }        public void clear()  //清空线性表    {        Arrays.fill(elementData, null);        size=0;    }        public String toString()  //以字符串输出    {        if(size==0)        {            return "[]";        }        else        {            StringBuilder sb = new StringBuilder("[");            for(int i=0;i<size;i++)            {                //取出一个元素加上一个逗号                sb.append(elementData[i].toString()+", ");//逗号+空格            }            //删除最后一个“逗号和空格”,加上"]"  [3,4,5,            int length = sb.length();            return sb.replace(length-2, length, "]").toString();        }    }    }

 

 
单链表
public class LinkList<T> {    //内部类Node代表节点    private class Node    {        private T data;  //保存节点的数据        private Node next; //指向下一个节点        public Node(){}        public Node(T data, LinkList<T>.Node next)         {            super();            this.data =http://www.mamicode.com/ data;            this.next = next;        }    }    private Node header; //保存该链表的头结点    private Node tail; //保存该链表的尾节点    private int size; //保存该链表的节点数        public LinkList()   //创建空链表    {        header = null;        tail = null;    }    public LinkList(T element)   //以一个元素来初始化链表    {        header = new Node(element,null); //只有一个节点        tail = header;        size++;    }    public int length()   //获取链表的长度    {        return size;    }        public T get(int index)  //根据索引获取某元素的数据    {        if(index<0 || index>size-1)        {            throw new IndexOutOfBoundsException("链表越界");        }        Node current = header;        for(int i=0;current!=null && i<size;i++)        {            if(i == index)            {                return current.data;            }        }        current = current.next;        return null;    }        public int indexOf(T element)  //获取链表中指定元素的索引    {        Node current = header;          for(int i=0;i<size && current!=null;i++ , current=current.next )   //要返回索引所以必须在循环里也写上i        {            if(current.data.equals(element))            {                return i;            }        }         return -1;    }    public void insert(T element,int index) //向线性表的指定索引后新增一个元素    {        if(index <0 || index>size)  //可以向链表的最后添加一个元素        {            throw new IndexOutOfBoundsException("线性表下标越界");        }        if(header == null) //如果还是空链表        {            addAtLast(element);//尾插法        }        else        {            //获取插入的前一个节点            Node pre = header;            for(int i=0;i<index-1;i++)            {                pre=pre.next;            }            pre.next = new Node(element,pre.next); //让新节点只想原来pre.next节点            size++;        }    }        private void addAtLast(T element)  //尾插法--被insert(T,int)调用    {        header = new Node(element,null);        tail = header; //只有一个节点,header和tail都指向它        size++;    }        public void addAtHeader(T element)//头插法    {        //总是以新节点作为头结点        header = new Node(element,header); //不管原来的头结点是否为空        size++;    }        public T delete(int index) //删除链式表指定处的元素    {        if(index<0 || index>size-1)        {            throw new IndexOutOfBoundsException("链表索引错误");        }                //删除头节点有点特殊        Node del = header;        if(index == 0)        {            header = header.next;            del.next=null;  //让原来的头结点的下一个指向null            //del = null;  //让原来的头结点失去引用        }        else        {            Node pre = header;            for(int i =0;i<index-1;i++) //让pre指向index-1节点            {                pre = pre.next;            }            del = pre.next; //del指向了index            pre.next = pre.next.next;            del.next=null; //让需要删除的节点的下一指向指向null                    }        size--;        return del.data;    }        public T deleteLast()   //删除最后一个元素    {        return delete(size-1);    }        public boolean empty() //判断链表是否为空    {        return size ==0;    }        public void clear()  //清空链表    {        //        Node pre =header;        Node current = pre.next;        for(;current!=null;current =current.next)        {            pre.next = null;            pre = current;        }        //pre.next = null;        tail = null;        size = 0;    }        public String toString()     {        if(empty())        {            return "[]";        }        else        {            StringBuilder sb = new StringBuilder("[");            for(Node current = header;current !=null;current=current.next)            {                sb.append(current.data.toString()+", ");            }            int len = sb.length();            return sb.replace(len-2, len, "]").toString();        }    }}

 

 
 
 
循环链表(circular linked list)---无头无尾链表 (从链表中任一节点出发均可以找到表中其他所有节点)
(算法实现更容易,让tail.next = header即可)
(头插法和尾插法均可)
(当使用单链表比较麻烦的时候,可以考虑这种实现)
 
 
 
双向链表(double linked list------DbLinkedList)
保存前驱引用和后继引用,所以维护成本略大
public class DbLinkedList<T> {        private class Node  //内部节点类    {        private T data;        private Node prev;        private Node next;        public Node(){}        public Node(T data, Node prev, DbLinkedList<T>.Node next)         {            super();            this.data =http://www.mamicode.com/ data;            this.prev = prev;            this.next = next;        }    }    private Node header;  //保存该链表的头结点    private Node tail; //保存链表的尾节点    private int size; //包含链表中已包含的节点数        public DbLinkedList()    {        //空链表的header 和 tail都是null        header = null;        tail = null;    }        public DbLinkedList(T element)  //以特定的元素构建链表    {        header = new Node(element,null,null);        //只有一个节点,header,tail都指向该节点        tail = header;        size++;    }        public int length()//返回链表的长度    {        return size;    }        public T get(int index)//获取链式线性表中索引为index处的元素    {        return getNodeByIndex(index).data;    }    private Node getNodeByIndex(int index) //根据索引获取    {        if(index<0 || index>size-1)        {            throw new IndexOutOfBoundsException("线性表下标越界");        }        if(index<=size/2)        {            //header节点开始            Node current = header;            for(int i=0;i<size/2&&current!=null;                    i++,current=current.next)//索引靠前就用            {                if(i==index)                {                    return current;                }            }        }        else        {            //从tail节点开始搜索            Node current = tail;            for(int i=size-1;i>size/2&&current!=null;                    i--,current=current.prev)            {                if(i == index)                {                    return current;                }            }        }        return null;    }        public int indexOf(T element)   //获取指定元素的下标    {        Node current = header;        for(int i=0;i<size && current!=null;i++,current=current.next)        {            if(current.data.equals(element))            {                return i;            }        }        return -1;    }            public void insert(T element,int index)//向指定位置插入一个元素    {        if(index<0 || index> size) //可向链表的最后一个元素后面插入元素        {            throw new IndexOutOfBoundsException("下标越界");        }                if(header==null)        {            addLast(element); //尾插法        }        else        {            //当index为0的时候,也就是在表头插入            if(index==0)            {                addAtHeader(element);            }            else //中间插入            {                Node prev = getNodeByIndex(index-1); //获取插入节点的前一个节点                Node next = prev.next; //获取插入节点                                Node newNode = new Node(element,prev,next);                                prev.next = newNode;                next.prev = newNode;                size++;            }        }    }    public void addLast(T element) //尾插法实现    {        if(header == null)        {            header = new Node(element,null,null);            tail = header;        }        else        {            //找到尾节点            Node newNode = new Node(element,tail,null);            tail.next = newNode;            tail = newNode;        }        size++;    }    public void addAtHeader(T element) //采用头插法    {        //创建新节点的next指向header,并把它作为新的list头        header = new Node(element,null,header);        if(tail == null)        {            tail = header;        }        size++;    }    public T delete(int index)  //删除指定索引的元素    {        if(index<0 || index>size -1)        {            throw new IndexOutOfBoundsException("线性表索引越界");        }        Node del = null;  //用于返回要删除元素的值                if(index==0)  //删除头结点        {            del = header;              header = header.next;   //让head指向原来的header的next             header.prev = null;  //新header的prev指向null        }        else        {            Node prev = getNodeByIndex(index-1); //获取要删除的前一个节点            del = prev.next;   //获取要删除的元素                        //开始处理互相引用            prev.next = del.next;            if(del.next!=null)            {                del.next.prev = prev;            }            del.prev = null;            del.next = null;        }        size--;        return del.data;    }        public T deleteLast() //删除链式线性表中的最后一个元素    {        return delete(size-1);    }        public boolean empty()  //判断是否为空    {        return size==0;    }        public void clear()    {        //前向和后项全部要置为null    }    public String toString()     {        if(empty())        {            return "[]";        }        else        {            StringBuilder sb = new StringBuilder("[");            for(Node current = header;current !=null;current=current.next)            {                sb.append(current.data.toString()+", ");            }            int len = sb.length();            return sb.replace(len-2, len, "]").toString();        }    }    public String reverseToString()    {        if(empty())        {            return "[]";        }        else        {            StringBuilder sb = new StringBuilder("[");            for(Node current = tail;current!=null;current = current.prev)            {                sb.append(current.data.toString()+", ");            }            int len=sb.length();            return sb.replace(len-2,len, "]").toString();        }    }}

 

 
 
 
小结:线性表就是加强操作的动态数组,充当容器的工具类。
Java中的实现(ArrayList,LinkedList,其中LinkedList还是双向链表)
----------------------------------------------------------------------------------------------------------------------------
 
 
 
 
栈和队列----功能弱化的线性表(功能受到限制,比方说增删只能在首尾)
(广度优先遍历的时候用到队列 保存节点)
(实现递归---栈)
 ---------------------------------------------------------------------------------------------------------------------------
Stack(栈--栈顶插入删除)
1. pop出栈、push入栈、LIFO
2. 常用操作:
     a. 初始化:构造器中,创建空栈
     b. 返回栈的长度:返回栈中元素的个数
     c. 入栈:栈顶插入一个元素,长度+1
     d. 出栈:栈顶删除一个元素,长度-1
     e. 访问栈顶元素:返回栈顶元素但是不删除元素
     f. 判断栈是否为空
     g. 清空栈
3. 顺序栈(当插入元素满了之后,要考虑数组容量重新定义)
import java.util.Arrays;public class SequenceStack<T>  //顺序栈{    private int DEFAULT_SIZE=10;    private int capacity; //数组容量    private int capacityIncrement = 0; //程序每次增加的数组长度    private Object[] elementData;     private int size=0; //当前元素的个数        public SequenceStack()  //创建一个空栈    {        capacity = DEFAULT_SIZE;        elementData = new Object[capacity];    }        public SequenceStack(T element) //用一个元素来初始化栈    {        this();        elementData[0] = element;        size++;    }        public SequenceStack(T element,int initSize,            int capacityIncrement) //指定增长容量    {        this.capacity = initSize;        this.capacityIncrement = capacityIncrement;                elementData = new Object[capacity];        elementData[0] = element;        size++;    }        public int length() //获取顺序栈的大小    {        return size;    }        public void push(T element) //入栈    {        ensureCapacity(size+1); //size+1即当前所需要的容量        elementData[size++] = element;    }    private void ensureCapacity(int minCapacity) //性能不是很好     {        if(minCapacity>capacity) //供不应求        {            if(capacityIncrement >0) //增量为正数            {                while(capacity<minCapacity)                {                    capacity += capacityIncrement;                }            }            else  //没有指定增长容量的时候,就直接采用翻倍的方法            {                while(capacity<minCapacity)                {                    capacity <<=1;                }            }            elementData = Arrays.copyOf(elementData, capacity); //        }    }        public T pop() //出栈:1.获取栈顶元素 2.栈元素的数量-1  3.原来栈顶引用为空    {        T oldValue = (T) elementData[size-1];        elementData[--size] = null;//这1步可写成2步:--size; elementData[size]=null        return oldValue;      }        public T peek() //取出栈顶元素    {        return (T)elementData[size-1];    }        public boolean isEmpty() //判断栈是否为空    {        return size==0;    }        public void clear() //清空栈 :数组元素的引用置为空;元素个数为空    {        Arrays.fill(elementData, null);        size = 0;    }        public String toString()    {        if(size==0)        {            return "[]";        }        else        {            StringBuilder sb = new StringBuilder("[");            for(int i=size-1;i>-1;i--) //需要倒序输出            {                sb.append(elementData[i].toString() + ", ");            }            int len = sb.length();            return sb.replace(len-2, len, "]").toString();        }    }}

 

4. 链栈(top保存栈顶元素的引用,size记录当前栈中保存元素的个数)
    入栈---头插法,size+1
    出栈---释放引用,size-1
(头插法实现了FILO)
public class LinkStack<T> {    private class Node    {        private T data;        private Node next; //指向下一个节点                public Node(){}        public Node(T data, LinkStack<T>.Node next)         {            super();            this.data =http://www.mamicode.com/ data;            this.next = next;        }    }    private Node top; //保存该栈的栈顶元素    private int size; //保存节点个数        public LinkStack()//空栈    {        top = null;     }    public LinkStack(T element) //只有一个元素的栈    {        top = new Node(element,null);        size++;    }    public int length() //获取栈的长度    {        return size;    }    public void push(T element) //入栈    {        top = new Node(element,top);        size++;    }    public T pop()    {        Node oldTop = top;        top = top.next;  //原来top的指向改变        oldTop.next = null;        size--;        return oldTop.data;    }        public T peek() //取栈顶元素    {        return top.data;    }        public boolean isEmpty() //判断是否为空    {        return size==0;    }    public void clear()    {        top = null;        size = 0;    }        public String toString()    {        if(isEmpty())        {            return "[]";        }        else        {            StringBuilder sb = new StringBuilder("[");            for(Node current = top; current!=null; current = current.next)            {                sb.append(current.data.toString()+", ");            }            int len = sb.length();            return sb.replace(len - 2, len, "] ").toString();        }            }    }
(整体而言,栈基本用不到顺序结构的随机访问功能,所以一般采用链栈)
(Java中Vector实现了List接口,Stack继承了Vector,所以Stack是线程安全的)
(Java.util.LinkedList提供了pop()、push()、peek(),所以它可以当做栈来使用)
 
-----------------------------------------------------------------未完待续----------------------------------------------------
 

数据结构和算法(Java版)