线性表

线性表:零个或多个元素的有限序列;

线性表_第1张图片
线性表.jpg

线性表的顺序存储结构

线性表的顺序存储结构:用一段地址连续的存储单元依次存储线性表的数据元素。

package dataStructure;
//线性表顺序存储结构
public class SeqList{
    public static int MAXSIZE = 20; //存储空间初始分配量
    private int length; //线性表当前长度
    private T[] data; //数组存储数据元素
    
    @SuppressWarnings("unchecked")
![Uploading 线性表_425942.jpg . . .]
    public SeqList(){
        data = (T[]) new Object[MAXSIZE];
    }
    //获取第i个元素
    public T getElem(int i){
        if(i<1 || i>getLength()){
            return null;
        }else{
            return data[i-1];
        }
    }
    
    //在位置i插入元素e
    public boolean ListInsert(int i,T e){
        //若顺序表已满
        if(getLength() == MAXSIZE){
            return false;
        }
        //i不在范围内
        if(i<1 || i>getLength()+1){
            return false;
        }else{
            //插入位置不在表尾
            if(i=i-1;j--){
                    data[j+1] = data[j];
                }
            }
            data[i-1] = e;
            setLength(getLength() + 1);
            return true;
        }
    }
    
    //删除第i个元素
    public T ListDelete(int i){
        //若表为空
        if(getLength() == 0){
            return null;
        }
        if(i<1 || i>getLength()){
            return null;
        }else{
            T e = data[i-1];
            //若不是最后一个
            if(i

测试代码:

package dataStructure;  
  
import java.util.Random;  
  
public class SeqListTest {  
    final int MAX = 25;  
    Random r = new Random();  
    SeqList seqList;  
      
    public SeqListTest() {  
        initSeqList();  
    }  
      
    //创建一个线性表顺序存储结构  
    public void initSeqList() {  
  
        seqList = new SeqList();  
//      int length = (int) Math.random();   //只能产生0.0 - 1.0之间的double随机数  
        int length = Math.abs(r.nextInt(MAX));  //使用Random随机产生一个25左右的值,使用Math.abs()函数来取绝对值    
        System.out.println("产生的数组长度为 :" + length);  
          
        if(length > SeqList.MAXSIZE) {  
            System.out.println("该长度不合法");  
        }  
          
        for (int i = 1; i <= length; i++) {  //为生成的数组赋值,同时也测试了插入值的方法  
            int j =r.nextInt(MAX);  
            System.out.print(j + " ");  
              
            if(!seqList.ListInsert(i, j)) {  
                System.exit(0);   
            }  
        }  
        System.out.println("\n原始数组是 :");  
        display(seqList);  
    }  
      
    //测试删除方法  
    public void deleteElem() {  
        int i = r.nextInt(MAX);  
        System.out.println("\n\n删除的位置是:" + i);  
        Integer deleteNumber = seqList.ListDelete(i);  
          
        if( deleteNumber == null) {  
            System.exit(0);  
        } else {  
            System.out.println("删除的元素是 : " + deleteNumber);  
            System.out.println("删除元素后数组是 :");  
            display(seqList);  
        }  
    }  
      
    //测试随机插入方法  
    public void insertByRandom() {  
        int i = r.nextInt(MAX);  
        System.out.println("\n\n随机插入位置是 :" + i);  
        int elem = r.nextInt(MAX);  
        System.out.println("随机插入数据是 :" + elem);  
        seqList.ListInsert(i, elem);  
        System.out.println("随机插入数据后数组是 :");  
        display(seqList);  
    }  
      
    //数据展示  
    public  void display(SeqList seqList) {  
        for (int i = 1; i <= seqList.getLength(); i++) {  
              
            if(seqList.getElem(i) != null) {  
                System.out.print(seqList.getElem(i) + " ");  
            }  
              
        }  
        System.out.println("数组的长度为 :" + seqList.getLength());  
    }  
      
    //获取元素  
    public void getElem() {  
        int i = r.nextInt(MAX);  
        System.out.println("\n获取位置为 :" + i);  
        System.out.println("获取到的元素为 : " + seqList.getElem(i));  
          
          
    }  
      
    public static void main(String[] args) {  
        SeqListTest s = new SeqListTest();  
        s.insertByRandom();  
        s.deleteElem();  
        s.getElem();  
    }  
} 

顺序存储优点:
1.无需为表示表中元素之间的逻辑关系而增加额外的存储空间。
2.对于存,读数据时时间复杂度都是O(1)。

顺序存储缺点:
1.插入和删除操作需要移动大量元素,时间复杂度为O(n)。
2.线性表长度变化较大时,无法确定存储空间的容量。

线性表的链式存储结构(单链表)

线性表的链式存储结构:用一组任意的存储单元存储线性表的数据元素。存储单元可以是连续的也可以是不连续的。

  • 头指针:若有头结点,则指向头结点;否则是指向第一个结点。无论链表是否为空,头指针都不为空。
  • 头结点:为了操作的方便而设立,在第一个元素之前。头结点的数据域不存储任何信息,指针指向第一个结点。若线性表为空,头结点的指针域为空。
  • 最后一个结点指针为null
package dataStructure;
//链表中的结点node
public class Node{
    private T data; // 数据域
    private Node next;//指针域
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    public Node getNext() {
        return next;
    }
    public void setNext(Node next) {
        this.next = next;
    }
}
package dataStructure;

public class LinkList{
    private Node head;//头结点
    private int length;//链表的长度
    
    public LinkList(Node head) {  
        this.head = head;  
    }  
    
    //获取第i个元素
    public T getElem(int i){
        int j = 1;
        Node n = head.getNext();//第一个元素
        //得到第i个结点
        while(n!=null && ji){
            return null;
        }else{
            return n.getData();
        }
        
    }
    
    //在位置i之后插入e
    public boolean ListInsert(int i,T e){
        int j = 1;
        Node n = head;
        //若为第一次插入
        if(head == null && i==1){
            head = new Node();
            head.setData(null);
            Node first = new Node();
            first.setData(e);
            head.setNext(first);
            length++;
            return true;
        }else{
            //得到第i个结点
            while(n!=null && ji){
                return false;
            }else{
                Node s = new Node();
                s.setData(e);
                s.setNext(n.getNext());
                n.setNext(s);
                length++;
                return true;
            }
        }
        
    }
    
    //删除位置i
    public T ListDelete(int i){
        int j = 1;
        Node n = head.getNext();
        //得到第i-1个结点
        while(n!=null && ji-1){
            return null;
        }else{
            T e = n.getNext().getData();
            n.setNext(n.getNext().getNext());
            length--;
            return e;
        }
        
    }
    
    public Node getHead() {
        return head;
    }
    public void setHead(Node head) {
        this.head = head;
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
}

测试代码:

package dataStructure;

import java.util.Random;

public class LinkListTest {
    final int MAX = 25;
    Random r = new Random();
    LinkList linkList;
    
    public LinkListTest() {
        initSeqList();
    }
    
    //创建一个线性表顺序存储结构
    public void initSeqList() {
        linkList = new LinkList(null);
        int length = Math.abs(r.nextInt(MAX));  //使用Random随机产生一个25左右的值,使用Math.abs()函数来取绝对值  
        System.out.println("产生的链表长度为 :" + length);
        
        for (int i = 1; i <= length; i++) { //为生成的链表赋值,同时也测试了插入值的方法
            int j =r.nextInt(MAX);
            System.out.print(j + " ");
            
            if(!linkList.ListInsert(i, j)) {
                System.exit(0); 
            }
        }
        System.out.println("\n原始链表是 :");
        display(linkList);
    }
    
    //测试删除方法
    public void deleteElem() {
        int i = r.nextInt(MAX);
        System.out.println("\n\n删除的位置是:" + i);
        Integer deleteNumber = linkList.ListDelete(i);
        
        if( deleteNumber == null) {
            System.exit(0);
        } else {
            System.out.println("删除的元素是 : " + deleteNumber);
            System.out.println("删除元素后链表是 :");
            display(linkList);
        }
    }
    
    //测试随机插入方法
    public void insertByRandom() {
        int i = r.nextInt(MAX);
        System.out.println("\n\n随机插入位置是 :" + i);
        int elem = r.nextInt(MAX);
        System.out.println("随机插入数据是 :" + elem);
        linkList.ListInsert(i, elem);
        System.out.println("随机插入数据后链表是 :");
        display(linkList);
    }
    
    //数据展示
    public  void display(LinkList linkList) {
        Node node = linkList.getHead();
        while(node != null) {
            System.out.print(node.getData() + " ");
            node = node.getNext();
        }
        System.out.println("链表的长度为 :" + linkList.getLength());
    }
    
    //获取元素
    public void getElem() {
        int i = r.nextInt(MAX);
        System.out.println("\n获取位置为 :" + i);
        System.out.println("获取到的元素为 : " + linkList.getElem(i));
        
        
    }
    
    public static void main(String[] args) {
        LinkListTest s = new LinkListTest();
        s.insertByRandom();
        s.deleteElem();
        s.getElem();
    }
}

虽然已上所有链表的操作时间复杂度都为O(n),但如果在第i个位置插入10个结点这种操作,对于顺序存储结构是每次插入都要移动n-i个点,每次都是O(n)。但对于链式存储结构,只要在第一次通过O(n)找到位置i,那么接下来插如的操作时间复杂度都是O(1)。
因此,对于插入或删除数据越频繁的操作,单链表的效率优势越明显。

  • 单链表的整表创建
package dataStructure;

public class CreateLinkList {
    // 头插法创建长度为n的链表
    public static LinkList createListHead(int n) {
        Node head = new Node();
        LinkList list = new LinkList(head);
        int j = 1;
        // 将新结点插入到头结点与前一新结点之间
        while (j <= n) {
            Node p = new Node();
            p.setData((int) (Math.random() * 25));
            System.out.println(p.getData());
            p.setNext(head.getNext());
            head.setNext(p);
            j++;
        }
        return list;

    }

    // 尾插法创建长度为n的链表
    public static LinkList createListTail(int n) {
        Node head = new Node();
        LinkList list = new LinkList(head);
        int j = 1;
        // 将结点插入到最后
        while (j <= n) {
            Node p = new Node();
            p.setData((int) (Math.random() * 25));
            System.out.println(p.getData());
            head.setNext(p);
            head = p;
            j++;
        }
        return list;
    }

    // 数据展示
    public static void display(LinkList linkList) {
        Node node = linkList.getHead();
        while (node != null) {
            System.out.print(node.getData() + " ");
            node = node.getNext();
        }
    }

    public static void main(String args[]) {
        display(createListHead(7));
    }
}
  • 单链表的整表删除
//整表删除
    public void clearList(){
        Node p = head;
        Node q = new Node();
        while(q != null){
            q = p.getNext();
            if(q!=null){
                p.setNext(q.getNext());
                length--;
            }
            
        }
    }

比较两种存储方式

线性表_第2张图片
Paste_Image.png
  • 若线性表要频繁查找,很少进行插入和删除,宜使用顺序存储结构。反之宜用单链表结构。
  • 当线性表中的元素个数变动较大或根本不知道有多大时,最好使用单链表。而如果事先知道线性表的大致长度,则考虑使用顺序结构。

静态链表

静态链表:用数组描述的链表。
每一个数组的元素有两个数据域:数据域与指针域(游标)。

package dataStructure.StaticLinkList;

public class Node{
    private T data;//数据域
    private int cur;//指针域
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    public int getCur() {
        return cur;
    }
    public void setCur(int cur) {
        this.cur = cur;
    }
}

一般数组的第一个元素和最后一个元素会被作为特殊元素处理,不存数据。
通常把未被使用的数据元素成为备用链表。

  • 数组的第一个元素的cur用来存放备用链表的第一个结点的下标。(即第一个没有数据的下标)
  • 数组的最后一个元素的cur用来存放第一个有数值的元素的下标,相当于单链表中的头结点作用。
package dataStructure.StaticLinkList;

@SuppressWarnings("unchecked")
public class StaticLinkList {
    public static int MAXSIZE = 20;
    private Node[] list;
    private int length;
    
    public StaticLinkList(){
        this.list = new Node[MAXSIZE];
    }
    // 初始化链表
    public void initList() {
        for (int i = 0; i < MAXSIZE; i++) {
            Node n = new Node();
            n.setCur(i+1);
            list[i] = n;
        }
        
        // 目前静态链表为空,因此最后一个元素的cur为0
        list[MAXSIZE-1].setCur(0);
    }

    // 在第i个元素位置插入e
    public boolean ListInsert(int i, T e) {
        //创建链表
        if((length == 0 && i ==1)|| i == length+1){
            list[i].setData(e);
            list[MAXSIZE-1].setCur(1);
            length++;
            return true;
        }
        if (i < 1 || i > length) {
            return false;
        }
        // 首先取得备用链表第一个结点的下标:
        int cur = list[0].getCur();
        // 同时改变第一个元素的cur,因为刚取出来的cur要被使用了,不再是备用链表的第一个结点了
        list[0].setCur(list[cur].getCur());

        list[cur].setData(e);

        int k = MAXSIZE-1;
        // 此时取出来的k为第i-1个元素的下标
        for (int j = 1; j <= i - 1; j++) {
            k = list[k].getCur();
        }
        // 第i个元素的下标
        int insert = list[k].getCur();
        list[k].setCur(cur);
        list[cur].setCur(insert);
        length++;
        return true;
    }

    // 删除第i个元素
    public T ListDelete(int i) {
        if (i < 1 || i > length) {
            return null;
        }
        int k = MAXSIZE-1;
        // 此时取出来的k为第i-1个元素的下标
        for (int j = 0; j < i - 1; j++) {
            k = list[k].getCur();
        }
        //第i个元素下标:
        int del = list[k].getCur();
        T e = list[del].getData();
        list[k].setCur(list[del].getCur());
        //更改第一个元素的cur与要删除元素的cur,因为要删除的元素变成了备用链表的第一个元素
        list[del].setCur(list[0].getCur());
        list[0].setCur(del);
        length--;
        return e;
        
    }

    //展示数据
    public void display(){
        int k = list[MAXSIZE-1].getCur();
        for(int i=1;i<=length;i++){
            T e = list[k].getData();
            k = list[k].getCur();
            System.out.print(e + " ");
        }
        System.out.println("链表的长度为 :" + getLength());
    }
    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public Node[] getList() {
        return list;
    }

    public void setList(Node[] list) {
        this.list = list;
    }

}

测试代码:

package dataStructure.StaticLinkList;

import java.util.Random;

public class StaticLinkListTest {
    StaticLinkList sllist = new StaticLinkList();

    // 用随机数初始化一个长度为n的静态链表
    public void init(int n) {
        sllist.initList();
        for (int i = 1; i <= n; i++) {
            sllist.ListInsert(i, (int) (Math.random() * 25));
        }
        display(sllist);
    }

    // 测试删除方法
    public void deleteElem() {
        int i = (int) (Math.random() * sllist.getLength());
        System.out.println("\n\n删除的位置是:" + i);
        Integer deleteNumber = sllist.ListDelete(i);

        if (deleteNumber == null) {
            System.exit(0);
        } else {
            System.out.println("删除的元素是 : " + deleteNumber);
            System.out.println("删除元素后链表是 :");
            display(sllist);
        }
    }

    // 测试随机插入方法
    public void insertByRandom() {
        int i = (int) (Math.random() * sllist.getLength());
        System.out.println("\n\n随机插入位置是 :" + i);
        int elem = (int) (Math.random() * 20);
        System.out.println("随机插入数据是 :" + elem);
        sllist.ListInsert(i, elem);
        System.out.println("随机插入数据后链表是 :");
        display(sllist);
    }

    // 展示list
    public void display(StaticLinkList sslist) {
        sslist.display();
    }

    public static void main(String[] args) {
        StaticLinkListTest s = new StaticLinkListTest();
        s.init(8);
        s.deleteElem();
        s.insertByRandom();
    }

}
  • 静态链表优点:
    在插入和删除时,只需改变游标而无需移动元素。
  • 静态链表缺点:
    没有解决连续存储分配内存空间难以确定的问题;
    失去了顺序存储结构随机存取的特性。

循环链表

将单链表中的终端节点的指针由空指针改为指向头结点,就使得整个单链表形成一个环,这种头尾相接的单链表为循环链表。

循环链表解决了从任何一个结点出发从而遍历到所有结点的问题。

循环链表与单链表的主要差异:循环的判断条件,单链表是判断p.next是否为空,循环列表则判断p.next是否为头结点。

在单链表中,在访问尾结点时需要O(n)的时间。但如果在循环链表中不使用头指针,而是使用尾指针rear,则访问第一个结点和最后一个结点都是O(1)。

双向链表

在单链表的每个结点中,都有一个前驱指针指向其前驱结点。所以双向链表中的结点都有两个指针域,一个指向后继,一个指向前驱。

双向链表使得之前查前驱结点的时间复杂度由O(n)变为O(1)。

但双向链表在插入和删除时都需更改两个指针。

你可能感兴趣的:(线性表)