线性表

Posted siyyawu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表相关的知识,希望对你有一定的参考价值。

线性表指的是具有相同类型的n(n>=0)个数据元素组成的有限序列。通常有顺序存储链式存储两种实现方式。

顺序存储通常用数组来实现,即占用一段连续的内存空间来关联数据的先后顺序。链式存储通常用指针来实现,即需要保存两个空间数据域和指针域的信息,用指针来关联线性表不同元素的内存地址,换句话来说,链式存储的数据不必保存在连续的内存空间中,指针描述了数据的先后顺序。

优缺点:

查找,顺序结构时间复杂度O(1)远低于链式结构O(n)

增加和删除,链式结构的时间复杂度O(1)低于顺序结构(最坏是O(n))

顺序存储实现SequenceList:

package SequenceList;


//顺序表
public class SqList<T>{

    private static final int DefaultSize=10; //初始化默认数组大小为10
    public int maxLength; //maxLength代表数组容量,即开辟了内存空间的数组大小
    public int size;   //size代表数组中存入的有效数据
    public Object[] element; //数据元素
    
    public SqList(T[] values)
    {
        maxLength=DefaultSize;   //保存数组容量
        element=new Object[DefaultSize];  //创建数组对象
        for(int i=0;i<values.length;i++)
        {
            element[i]=values[i];
        }
        size=values.length;
    }
    
    //遍历
    public void traversal()
    {
        for(int i=0;i<size;i++)
        {
            System.out.println(element[i]);
        }
    }
    
    //判断是否为空
    public boolean isEmpty()
    {
        return size==0;
    }
    
    //判断数组容量是否装满了
    public boolean isFull()
    {
        return size==maxLength;
    }
    
    //在第index位置后插入x,O(n)
    public int insert(int index,T x)
    {
        if(index<=0)
        {
            index=0;
        }
        if(index>maxLength-1)
        {
            index=maxLength;
        }
        
        if(isFull())  //判断是否数组是否装满了
        {
            Object[] source=new Object[maxLength];  //扩大数组容量为当前容量的两倍
            for(int i=0;i<size;i++)
            {
                source[i]=element[i];
            }
            maxLength=maxLength*2;
            System.out.println("容器已满,maxLength扩大为:"+maxLength);
            element=new Object[maxLength];
            for(int i=0;i<size;i++)
            {
                element[i]=source[i];
            }
        }
        
        
        for(int i=size;i>index;i--)
        {
            element[i]=element[i-1];
        }
        element[index]=x;
        size++;
        return index;
    }
    
    //在第index位置删除元素,O(n)
    public void deleteElem(int index)
    {
        for(int i=index;i<size-1;i++)
        {
            element[i]=element[i+1];
        }
        size--;
    }
    
    //设置第index位置的元素
    public void set(int index,T x)
    {
        element[index]=x;
    }
    
    
    //获取第index位置的元素
    public T get(int index)
    {
        return (T)element[index];
    }
    
    
    //清空
    public void clear()
    {
        size=0;
    }
}

测试程序:

package SequenceList;

public class App {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        Integer[] a={1,2,3,4,5,6,7,8,9,10};  
        SqList<Integer> sqList=new SqList<>(a); //初始化顺序表
        System.out.println("当前maxLength:"+sqList.maxLength);
        System.out.println("当前size:"+sqList.size);
        System.out.println("插入前遍历");
        sqList.traversal();
        sqList.insert(10, 100); //在第10个元素后插入100,数组容量超出
        System.out.println("插入后,当前maxLength:"+sqList.maxLength);
        System.out.println("插入后,当前size:"+sqList.size);
        System.out.println("插入后遍历");
        sqList.traversal();
    }

}

结果:

当前maxLength:10
当前size:10
插入前遍历
1
2
3
4
5
6
7
8
9
10
容器已满,maxLength扩大为:20
插入后,当前maxLength:20
插入后,当前size:11
插入后遍历
1
2
3
4
5
6
7
8
9
10
100

 


 

在早期的编程语言中,没有C语言的指针,也没有其他高级语言的对象引用机制,聪明的人想到仍然可以用数组实现链式存储,即保存一个“伪指针”来指向数组的下标来实现指针的功能。

技术分享图片

这就是静态链表,第一个结点和最后一个结点都不保存数据,只保存指针。其中第一个结点用来指向备用链表的第一个空间的坐标(即未保存数据的空间), 最后的结点(下标为Length-1的位置)用来指向第一个插入元素位置的坐标,充当头结点(即图中下标为1的位置)。

静态链表的实现StaticLinkedList:

package StaticLinkedList;

import org.junit.Test;


//静态链表
public class SLList{

    private static final int DefaultSize=100;//初始化100个空间
    public element[] link=null;
    public int current=0; //备用链表坐标
    public int head=0; //头结点坐标
    public int length=0;//链表长度    
    public int Maxlength=0;


    //初始化
    public SLList()
    {
        Maxlength=DefaultSize;
        link=new element[DefaultSize];
        for(int i=0;i<20;i++)  //初始化有用链表的空间
        {
            link[i]=new element();
            link[i].setCur(i+1);
            link[i].setData(i);;
        }
        link[20]=new element();
        link[20].setData(link[19].getData()+1);
        link[20].setCur(0);
        for(int i=21;i<Maxlength-1;i++) //初始化备用链表的空间
        {
            link[i]=new element();
            link[i].setCur(i+1);
        }
        length=20;
        link[0].setCur(21);  //第一个元素指向备用链表的第一个空间
        link[Maxlength-1]=new element();
        link[Maxlength-1].setCur(1); //
    }

    //遍历
    public void traversal()
    {
        getElementTraversal(link[Maxlength-1]);
    }
    
    
    //递归遍历
    public void getElementTraversal(element e)
    {
        if(e.getCur()==0)
        {
            return;
        }
        else
        {
            System.out.println(link[e.getCur()].getData());
            getElementTraversal(link[e.getCur()]);
        }
    }


    //获取链表第index个元素,递归访问
    public element get(int index)
    {
        return link[getElementCur(index,link[Maxlength-1])];
    }

    public int getElementCur(int index,element e)
    {
        if(index-1==0)
        {
            return e.getCur();
        }
        else
        {
            index--;
            return getElementCur(index, link[e.getCur()]);
        }
    }


    //在第index元素后增加元素
    public void addElement(int indx,int data)
    {
        //新增元素地址cur1
        int cur1=link[0].getCur();
        link[cur1]=new element();
        link[cur1].setData(data);
        //新增元素Cur指向index后的元素
        link[cur1].setCur(get(indx).getCur());
        //index元素指向新增元素
        get(indx).setCur(link[0].getCur());
        //link[0]指向备用链表为空的位置
        link[0].setCur(link[0].getCur()+1);;
    }
}

class element{
    private int data;
    private int cur;
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public int getCur() {
        return cur;
    }
    public void setCur(int cur) {
        this.cur = cur;
    }
}

测试程序:

package StaticLinkedList;

public class App {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        SLList slList=new SLList();
        System.out.println("插入前遍历");
        slList.traversal();
        element e=slList.get(3);
        System.out.println("查找第3个位置的元素");
        System.out.println("cur:"+e.getCur()+"   data:"+e.getData());
        slList.addElement(2,100);
        System.out.println("插入后遍历");
        slList.traversal();
    }

}

结果:

插入前遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
查找第3个位置的元素
cur:4   data:3
插入后遍历
1
2
100
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

 


 

在C语言出现之后,指针能比较方便的描述链式结构。链式结构为了方便一些查找和遍历的操作,可以保存头指针和尾指针来辅助。链式结构分为单向链表和双向链表,单向链表通常一个结点只保存下一个对象的指针。双向则同时保存上个结点和下个结点的指针。同时,多个链表也能组成循环链表,比如将第一个链表的尾节点指向第二个链表的头结点,第二个链表最后的结点指向第一个链表的头结点,这样就形成了一个封闭的环结构。

单链表的插入通常有头插法和尾插法两种方式,头插法的头结点永远指向链表的新增结点上,即新增结点在链表的首位。当然这样的插入方式在遍历时会发现先插入的结点后输出,是一种栈结构。

而通常符合我们的思维的是新插入结点排在上个插入结点的后面,这种插入方式称为尾插法,可以保存一个尾节点指针来插入数据。

单链表的实现:

package SingleLinkedList;

import java.util.Random;


//链式存储 单链表
public class SLList{

    
    public SLList()
    {
        head=new SLListNode<Integer>(null);
        tail=new SLListNode<Integer>(null);
        tailInsertInit();
    }
    
    //头指针
    public SLListNode<Integer> head;
    //尾指针
    public SLListNode<Integer> tail;
    
    
    //节点数量
    public int length;
    
    
    //遍历
    public void traversal()
    {
        if(head!=null)
        {
            SLListNode<Integer> tempNode=head;
            while(tempNode.next!=null)
            {
                tempNode=tempNode.next;
                System.out.println(tempNode.data);
                
            }
        }
        else
        {
            return;
        }
        
    }
    
    
    //头插法初始化
    public SLListNode<Integer> headInsertInit()
    {
        Random random=new Random();
        for(int i=0;i<20;i++)
        {
            int temp=random.nextInt(100);
            SLListNode<Integer> newNode=new SLListNode<>(i);
            length++;
            if(head==null)
            {
                head.next=newNode;
            }
            else
            {
                newNode.next=head.next;
                head.next=newNode;
            }
        }
        return head;
    }
    
    
    //头插法
    public SLListNode<Integer> headInsert(SLListNode<Integer> Node)
    {
        if(head!=null)
        {
            Node.next=head.next;  //插入结点的指针指向头结点指向的结点
            head.next=Node;       //头结点指向插入结点,始终保持指向新插入的结点
        }
        else
        {
            head.next=Node;
        }
        length++;
        return head;
    }
    
    
    //尾插法初始化
    public SLListNode<Integer> tailInsertInit()
    {
        Random random=new Random();
        for(int i=0;i<20;i++)
        {
            int temp=random.nextInt(100);
            SLListNode<Integer> newNode=new SLListNode<>(i);
            length++;
            if(head.next==null)
            {
                head.next=newNode;
                tail.next=newNode;
            }
            else
            {
                tail=tail.next;
                tail.next=newNode;
            }
        }
        return head;
    }
    
    //尾插法
    public SLListNode<Integer> tailInsert(SLListNode<Integer> Node)
    {
        if(head.next!=null)
        {
            tail=tail.next;         //找到尾指针
            tail.next=Node;         //尾指针指向新增结点
        }
        else
        {
            head.next=Node;          
            tail.next=Node;
        }
        length++;
        return head;
    }
    
    //在第index个结点后插入x
    public int insertByIndex(int index,SLListNode<Integer> x)
    {
        if(index<=0)
        {
            x.next=head.next;
            head.next=x;
        }
        int i=0;
        SLListNode<Integer> tempNode=head;
        while(i<index)
        {
            tempNode=tempNode.next;
            i++;
        }
        x.next=tempNode.next;
        tempNode.next=x;
        return index;
    }
    
    
    //删除第index个结点
    public SLListNode<Integer> deleteByIndex(int index)
    {
        if(index<=0)
        {
            head.next=head.next.next;
        }
        int i=0;
        SLListNode<Integer> tempNode=head;
        while(i<index-1)
        {
            tempNode=tempNode.next;
            i++;
        }
        tempNode.next=tempNode.next.next;
        return head;
    }
    
}

//单链表
class SLListNode<T>{

    public T data;
    public SLListNode<T> next;
    public SLListNode(T data)
    {
        this.data=data;
    }
}

 

测试:

package SingleLinkedList;

public class App {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        SLList slList=new SLList();  //使用尾插法
        System.out.println("插入前遍历");
        slList.traversal();
        slList.insertByIndex(2, new SLListNode<Integer>(200)); 
        System.out.println("插入后遍历");
        slList.traversal();
    }
}

 

结果:

插入前遍历
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
插入后遍历
0
1
200
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

 

以上是关于线性表的主要内容,如果未能解决你的问题,请参考以下文章

如何在android中的地图片段内中心线性布局?

垂直线性布局中的多个片段

线性表的插入和删除操作代码(C语言)

在android中的类内的对话框片段的线性布局中添加textview

数据结构学习笔记二线性表---顺序表篇(画图详解+代码实现)

数据结构学习笔记二线性表---顺序表篇(画图详解+代码实现)