码迷,mamicode.com
首页 > 其他好文 > 详细

线性表

时间:2014-12-21 10:19:00      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:

线性表(顺序存储、链式存储):一对一松耦合关系。(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 = 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 = 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还是双向链表)


线性表

标签:

原文地址:http://www.cnblogs.com/bluechip/p/4176192.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!