线性表入门教程

  1. 总结《大话数据结构》写个教程记录供日后方便查看

1.线性表定义:零个或多个相同的数据元素的组成有限序列

简单说明下:线性表是一个序列,元素之间有顺序。第一个元素无前驱,最后一个元素无后继,其他元素有且只有一个前驱和后继。并且数据类型相同。

在较复杂的线性表中,一个数据元素可以由若干个数据项组成

2.线性表存储结构

2.1.顺序存储结构

2.1.1顺序存储定义

用一段地址连续的存储单元依次存储线性表的数据元素。物理结构上连续

2.1.2顺序存储方式

在内存中,申请一块连续空间,按照顺序进行存储的方式。说白了,就是在内存中找块地,通过占位的方式,把一定的内存空间给粘了,然后把相同数据类型的数据元素依次放在这块空地。用一维数据来实现顺序存储结构,把第一个数据元素存到数组下标为0的位置中。

C语言线性表的顺序存储的结构代码

#define MACSIZE 20 //存储空间初始分配量

typedef int ElemType //ElemType类型根据实际情况而定,这里假设为int
typedef struct{
    ElemType data[MAXSIZE] ;//数组存储元素
    int length;//线性表当前长度
}

顺序存储结构三个属性:

1.存储空间的起始位置:数组data

2.线性表的最大存储容量:数组长度Maxsize

3.线性表的当前长度:length

 

JAVA顺序存储结构

import java.util.Arrays;

/**
 * 线性表的顺序存储结构
 */
public class SqList{
    private  int maxSize;//数组长度,即最大存储空间
    private int ElemType;/* ElemType类型根据实际情况而定,这里假设为int*/
    private int length;/*线性表当前长度*/
    private Object[] data;//存储数据元素
    public SqList(){
        //初始化,默认为20
        maxSize=20;
        data=new Object[20];
        length=0;
    }
    /**
     * 获取length
     */
    public int getlength(){
        return length;
    }

    /**
     * 自定义长度
     * @param maxSize
     */
    public SqList(int maxSize){
        if(maxSize<0){
            throw  new RuntimeException("数据大小为负,初始化失败!");
        }else {
            this.maxSize=maxSize;
            data=new Object[maxSize];
            length=0;
        }

    }

    /**
     * 对元素为空进行判断
     * 判断元素是否存在
     */
    public  boolean containelem(Object elem){
        if(elem!=null){
            return false;
        }
        for(int i=0;i<length;i++){
            if(data[i].equals(elem)){
                return true;

            }
        }
        return false;
    }

    /**
     * 1.取出位置不符合,抛异常
     * 2.取出元素
     * @param i 是下标索引
     * @return
     */
    public Object getelem(int i){
        if(i>length&&i<0){
            return "error";
        }
        return data[i];

    }

    /**
     *1.删除位置不合理,抛异常
     *2.取出元素
     * 3.从删除元素位置开始遍历到最后一个位置,分别将他们像前移动一个位置
     * 4.表长减1
     */
    public String deletesql(int i,Object elem){
        if(length==0){
            return "Error";
        }
        if(i>length&&i<1){
            return "error";
        }

        if(i<length){
           for(int j=i;j<length-1;j++){
               data[j-1]=data[j];
           }

          length--;
        }
        return "OK";
    }

    /**
     * 初始化条件顺序表存在,1<i<<length;
     * 操作结果,返回第i个元素值
     * @param i
     * @param elem
     * @return
     */
    public String GetElem(int i,Object elem){
        if(i>maxSize&&i<0){
            return "Error";
        }
        elem=data[i-1];
        return "ok";
    }

    /**
     * 插入算法思路:
     * 1.插入不合理,抛出异常
     * 2.如果线性表长度大于或等于数组长度,抛出异常,或者动态加入容量
     * 3.从最后一个位置开始向前遍历到第一个位置,分别将他们向后移动一位
     * 4.将要插入元素填入位置i处
     * 5.表长加1
     *
     *注意参数i是从1开始的。
     *
     *
     * @param elem
     */
    public String insertElem(int i,Object elem){

        if(length==maxSize){
            return "Err";
        }
        //1.
        if(i>length+1||i<1){
            return "err";
        }

        //3.
            for(int j=length;j<i-1;j--){
                if(j>i-1){
                    data[j+1]=data[j];
                }
            }
            data[i-1]=elem;
            //5
            length++;
        return "OK";
    }

    /**
     * 在数组data后面插入某个元素
     * 注意:elem 不能为空,length 不能超过maxsize
     * 思路:添加元素
     * length加1
     * @return
     */
    public boolean insertElem(Object elem){
     if(data==null){
         return false;
     }
     if(length<maxSize){
         data[length]=elem;
         length++;
         return true;
     }
     return false;

    }

    public void print(){
        System.out.println(Arrays.toString(data));
    }

    public int getMaxSize() {
        return maxSize;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }
}



/**
 * 两个链表求交集 A U B
 * 思路:
 * 遍历B链表,查看A链表中是否存在这个元素,不存在,插入这个元素
 * 注意顺序链表A 初始化内容是A+B之和
 */
public class Union {
    public  void initunion(SqList a,SqList b){
         for (int i=0;i<b.getlength();i++){
             Object obj=b.getelem(i);
             if(!a.containelem(obj)){
                 //数组长度不能超过链表长度
                 if(a.getlength()<a.getMaxSize()){
                     a.insertElem(obj);
                 }
             }
         }

    }
}

public class Main {

    public static void main(String[] args) {
        SqList sqList=new SqList();
        for (int i=0;i<10;i++){
            sqList.insertElem(i+1,i);
        }
        SqList sqList2=new SqList();
        for (int i=10;i<15;i++){
            sqList2.insertElem(i);
        }

//        sqList.print();
//        sqList2.print();
//        Object t=new Object();
//        sqList.deletesql(2,t);
//        if(t instanceof Integer){
//            System.out.println((Integer)t);
//        }
//        sqList.print();
//        Object getobj=sqList.getelem(3);
//        if(getobj instanceof Integer){
//            System.out.println((Integer)getobj);
//        }
        Union union=new Union();
        union.initunion(sqList,sqList2);
        sqList.print();
        sqList2.print();


    }

}


时间复杂度:

最好的情况,如果元素插入到最后一个位置,或者删除最后一个位置,此时的时间复杂度为O(1),因为不需要移动元素。如同来了一个新人要正常排队,当然是排到最后,如果此时他想离开,不会影响其他人,最坏的情况是插入或者删除第一个元素,此时需要移动n-1次。由于元素插入到第i个元素,或者删除第i个元素,需要移动n-i次,平均移动n-2/2.

由此得出结论,平均时间复杂度O(n)

优缺点:

优点:无需为表示逻辑关系添加额外的存储空间,可以快速查询任何位置的元素

缺点:插入和删除操作需要移动大量的元素。难以确定长度,造成空间碎片。

 

 

2.2链式存储结构

 

特点

用一组任意的存储单元存储线性表的数据元素,这组存储可以是连续的,也可以是不连续的。这就意味着,这些数据元素可以存在内存未被占用的任意位置。

定义:

为了表示每个数据元素ai与直接后继数据元素ai+1之间的逻辑关系对数据元素ai来说,除了存储其本身的信息之外,还需要存储一个指示其直接后继的信息。存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域,指针域中存储的信息称为指针或链。这两部分信息组成数据集元素ai的存储映像,称为结点(Node)。

n个结点(ai的存储映像)链结成一个链表,即为链表(a1,a2,.....an)的链表式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表。

链表中的第一个结点的存储位置叫头指针,那么正规链表的存取就必须从头指针开始进行。之后的每个结点,其实就是,就是上一个后继指针指向的位置。

最后一个,就意味着后继不存在,线性链表的最后一个结点指针为“空”

头指针与头结点的异同

头指针:

头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。

头指针具体标识作用,所以常用头指针冠以链表的名字

无论链表是否为空,头指针均不能为空,头指针是链表的必要元素

头结点:

头结点是为了操作的统一和方便而设立的,放在第一个元素结点之前,其数据域一般无意义

有了头结点,对在第一个元素结点前插入结点和删除第一结点,其操作与其他结点的操作就统一

头结点不一定是链表必须要素

 

 线性表链式存储结构代码描述

若线性表为空,则头结点的指针域为“空”。

如图表示线性表中的数据元素及数据元素之间的逻辑关系。

2.2.1单链表

单链表中,结构指针

typede struct Node{
ElemType data;
struct Node * next;
}Node;
typedef struct Node *LinkList;//定义LinkList

Java代码

/**
 * 单链表的存储结构
 */
public class LinkList {

    private NodeSql head;//定义头结点

    public LinkList() {
        head = new NodeSql();//
    }


    /**
     * 获取链表第一个数据的算法思路
     * 1.声明一个节点p,初始化j=1
     * 2.
     * 3.j<i 结点向后移动,j+1
     * 4.若p到到末尾没有找到元素,表示元素不存在
     * 5.返回指定元素
     *
     * @param i
     * @param elem
     */
    public Object getElem(int i, Object elem) {
        NodeSql p;//指向第一个元素
        int j=1;
        p=head;
        while (p.next!=null&&j<i){
            p=p.next;
            ++j;
        }
        elem=p.data;
        return elem;


    }

    /**
     * 思路:在i位置插入元素
     * 1.声明一个节点p,初始化讲
     * 2.p指向第一个结点‘
     * 3.p不为空j<i,进行移动 p=p.next j++
     * s.next=p.next
     * p.next=s
     */
    public void insertelem(int i, NodeSql s) {
        NodeSql p;//初始化一个结点p,指向第一个结点
        int j=1;
        p=head;
        while (p.next!=null&&j<i){
            p=p.next;
            ++j;
        }
        if (p==null||j>i) return;
        s.next=p.next;
        p.next=s;


    }

    /**
     * 添加结点
     * 为空判断
     * @param elem
     */
    public void addNode(NodeSql elem){
        NodeSql temp=head;
        while (temp.next!=null){
            temp=temp.next;
        }
        temp.next=elem;
    }

    public  void print(){
        NodeSql temp=head;
        while (temp!=null){
            System.out.println(temp.data);
            temp=temp.next;
        }

    }

    /**
     * 删除结点
     * 初始化j=1
     * 循环 找到第i个结点
     * 找到某个结点
     * 删除p->next=p->next->kkonext
     * @param i
     * @return
     */
    public boolean deleteelem(int i){
        NodeSql p;
        int j=1;
        p=head;
        while (p.next!=null&&j<i){
            p=p.next;
            ++j;

        }
        if(p==null||j>i){
            return false;
        }
        p.next=p.next.next;
        return true;

    }
}

2.2.2 静态链表

特点:

用数组替代指针,描述单链表

定义:数组的元素由两个数据域组成,data和cur。游标cur相当单链表中next,存放该元素的后继在数组中的下标。用数组描述的链表叫静态链表。

结构:

2.2.3 循环链表

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

结构:

/**
 * 循环单链表
 */
public class CilcleList {
    private  NodeSql head,rear;//头、尾结点
    private int size=0;

    public CilcleList(){
        head=new NodeSql();
        rear=head;
        rear.next=head;//尾结点指向头指针
    }

    /**
     * 思路:初始化变量p ,p 指向头结点,
     * 初始化变量j=1;
     * 判断是否p->next!=rear j<i 找到i的位置,p=p->next
     * new 一个结点Nodesql  s,将值赋值进去
     * s.data=elem
     * s.next=p.next
     * p.next=s;
     * return ok
     * @param i
     * @param elem
     */
    public boolean addList(int i,Object elem){
        NodeSql p;
        int j=1;
        p=head;//p为头结点
        while (p.next!=rear&&j<i){
            p=p.next;
            ++j;
        }
        if(size>0){
            //p.next=rear说明指向最后一个结点,
            if(j>i){
                return false;
            }
        }
        NodeSql s=new NodeSql(elem);
        s.next=p.next;
        p.next=s;
        size++;
        return true;

    }

    /**
     * 获取第i个位置的元素,返回elem中
     * 思路:1.初始化结点p 和j=1
     * 2.判断p.next!=rear j<i,移动j元素,找到第i个位置,p=p.next,j++,
     * 3.返回元素 elem=p.data
     * @param i
     * @param elem
     */
    public void getElem(int i,Object elem){
        NodeSql p;
        int j=1;
        p=head;
        while (p.next!=rear&&j<i){
            p=p.next;
            j++;
        }
        if(p.next==rear||j>i){
            return;
        }
        elem=p.data;
    }

    /**
     * 获取所有的元素
     * 1.初始结点p
     * 2.判断p.next!=rear,遍历链表p=p.next,打印值
     */
    public void getallelem(){
        NodeSql p;
        p=head;
        while (p.next!=rear){
           p=p.next;
            System.out.print(p.data);
        }

    }

    /**
     * 删除第i个位置的元素
     * 思路:
     * 1.初始化p j=1;
     * 2.p指向头结点
     * 3.判断p.next!=rear j<i找到第i个结点位置p=p.next j++
     * p.next=p.next.next
     * 4.清除结点
     * @param i
     * @return
     */
    public  boolean deleteElem(int i){
        NodeSql p,q=null;
        int j=1;
        p=head;
        while (p.next!=rear&&j<i){
            p=p.next;
            j++;
        }
        if(j>i){
            return false;
        }
        q=p.next;
        p.next=q.next;
        q=null;
        return true;
    }

    /**
     * 合并两个链表
     * 思路:
     * 找到 pr 为this 的尾指针reara,头结点p=reara.next,
     * 找到qr的尾指针,那么rerab的头结点为rerab=qr.next
     * pr 的尾结点指向qr的头结点
     * reara.next=rerab.next.next/指向qr的第一个结点,不是头结点
     * rearb.next=p;
     * @param
     * @param qr
     */
    public void union(CilcleList  qr){
        NodeSql p;
        NodeSql reara=this.rear;//pr的尾结点
        NodeSql rearb=qr.rear;//qr的尾结点
        p=reara.next;//ra的头结点
        reara.next=rearb.next;
        rearb.next=p;//将原a的头结点,赋值给 rearb.next

    }


}

2.2.4双向链表

定义:双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。

特点:双向链表是单链表扩展出来的结构,所以很多操作和单链表相同。双向链表可以做反向遍历查找等数据结构。

/**
 * 约瑟夫问题:
 * * 描述:n个小朋友按编号1、2、3.......n围成 一个圈坐着,按照顺时针方向从K的位置,从1开始依次报数,数到m位置的小朋友出局,
 * 再从m后面一个小朋友依次数数。直到所有的小朋友出局
 * * 思路:第一步:创建一个具有n个结点不带头结点的双向循环链表代码1到n个小朋友
 * 2.找到k个位置的人从1开始报数,找到第k个结点
 * 3.编号为k的人开始报数,并开始报数,报到m开出列,即删除该结点
 */
public class YueSefu {
    private DulNode head;
    private int size = 0;

    public YueSefu() {
    }


    public void addElem(int i, Object elem) {
        int j = 0;
        DulNode p = null;
        if (size == 0) {//第一个结点
            head = new DulNode(elem);
            p = head;
            head.prior=head;
            head.next = p;//尾结点指向头结点
            head.flag=true;
            return;
        }
        while (p.next != head && j < i) {
            p = p.next;
            j++;
        }
        if (p == null || j > i) {
            return;
        }
        DulNode s = new DulNode(elem);
        s.prior = p;
        s.next = p.next;
        p.next.prior = s;
        p.next = s;

    }

    /**
     * 2找到第k个结点
     * 思路:初始化p j=1;
     * 进行查找k个之后开始计数
     *
     * @param k
     */
    public void findindex(int k, int m) {
        DulNode p;
        p = head;
        int j = 1;
        int cont = 1;
        if (k > 1) {
            while (p!=null) {
                //找到k的位置,开始计算
                if (j < k) {
                    p = p.next;
                    j++;
                }else {
                    if (cont < m) {
                        p = p.next;
                        cont++;

                    } else {
                        cont = 1;
                        //开始移除某个元素
//                        deleteelem(p.data);

                        getAllElem();
                        System.out.println(" 需要删除的 "+p.data);
                        //重新定位头结点
                        if(p==head){
                            head=p.next;

                        }
                       // 移除某个结点
                        p.next.prior=p.prior;
                        p.prior.next=p.next;
                        if(p.next==p&&p.next.prior==p){
                            p=null;
                        }


                    }
                }

            }
        }
    }

    /**
     * 移除某个结点
     *
     */

    /**
     * 移除某个位置的元素
     * 思路:初始化p=head,
     *
     * @param i
     * @return
     */
    public boolean deleteElem(int i) {
        DulNode p;
        int j = 1;
        p = head;
        while (j < i) {
            p = p.next;
            j++;
        }
        if (j > i) {
            return false;
        }
        p.prior.next = p.next;
        p.next.prior=p.prior;
        return true;
    }
    /**
     * 移除某个元素
     */
    public  boolean deleteelem(Object elem){
        DulNode p;
        p=head;
        while (p!=null){
            if((p.data).equals(elem)){
                p.next.prior=p.prior;
                p.prior.next=p.next;
                return true;
            }if(p.next==p&&p.next.prior==p){
                p=null;
                return false;
            }
            else {
                p=p.next;
            }

        }
        return false;
    }


    /**
     * 添加n个结点
     * 思路:初始化n个结点
     *
     * @param n
     */
    public void addAllElem(int n) {
        DulNode p = null;
        if (size < n) {
            int j = 1;
            while (j <= n) {
                if (size == 0) {
                    head = new DulNode(j);
                    head.prior=head;
                    head.next = head;
                    head.prior=head;
                    p = head;
                    j++;
                    size++;
                } else {
                    DulNode s = new DulNode(j);
                    s.prior = p;
                    s.next = p.next;
                    p.next.prior = s;
                    p.next = s;
                    p = s;
                    j++;
                    size++;
                }
            }
        }
    }

    /**
     * 遍历元素
     */
    public void getAllElem() {
        DulNode p = head;
        while (p!=null) {
            System.out.print("  "+p.data);
            if (p.next==head){
                p=null;
            }else {
                p = p.next;
            }

        }

    }

}

 

单链表的例子

/**
 * 单链表的应用
 * 利用单链表就两个集合的差集A-B ,即所有的属于集合A而不属于集合B的元素。
 * 思路:1.初始化单链表A
 */
public class Chaji {
    private NodeSql head;

    private  static   int size;
    public Chaji(){
        size=0;
    }

    /**
     * 尾部进行插入
     *
     * @param elem
     */
    public void addHeadElem(Object elem) {
        if (elem != null) {
            NodeSql p;
            p = head;

            if (size == 0) {
                head = new NodeSql(elem);
                size++;
            } else {
                while (p.next != null) {
                    p = p.next;
                }
                NodeSql s = new NodeSql(elem);
                p.next = s;
                size++;
            }

        }
    }

    /**
     * 求差价
     * 时间复杂度()
     */
    public void ChaJiElem(Chaji lb) {
        //遍历链表lb,la看是否存在,如果存在删除该元素
        NodeSql p=lb.head;
        //遍历lb链表
        while (p!=null){
            //1.判断lb元素是否存在la链表中

            if(IsExsitElem(p.data)){
                deleteelem(p.data);
            }
            if (p.next==null){
                p=null;
            }else {
                p=p.next;

            }


        }
    }

    /**
     * 遍历表判断元素是否存在在链表中
     *
     * @param elem
     * @return
     */
    public boolean IsExsitElem(Object elem) {
        NodeSql p;
        p = head;
        if (elem == null) {
            return false;
        }
        while (p.next != null) {
            if ((p.data).equals(elem)) {
                return true;
            }
            p = p.next;
        }
        return false;

    }
    /**
     * 删除某个元素
     */
    public  boolean deleteelem(Object elem){
        NodeSql p;
        p=head;
        while (p.next!=null){
            //删除某个元素
            if((p.next.data).equals(elem)){
                p.next=p.next.next;
                return true;
            }
            p=p.next;
        }
        return false;
    }

    /**
     * 删除某个结点
     * 删除某个元素
     */
    public boolean deleteNode(NodeSql node){
        return  false;
    }

    public void getAllElem() {
        NodeSql p;
        p = head;
        while (p != null) {
            System.out.print(p.data+" ");
            if (p.next==null){
                p=null;
            }else {
                p = p.next;
            }


        }
    }

}

循环单链表demo

/**
 * 已知一个带哨兵结点h的循环链表中的元素,有正数和负数,
 * 试着编写一个算法,构造两个循环单链表,一个单链表只含正数,一个单链表只含负数。
 * 分析:初始时,先创建两个空单链表ha和hb,然后依次遍历链表,如果是正数插入到ha中,如果是负数插入到hb中
 */

/**
 * 步骤:1.先初始化一张循环链表h,既有正数又有负数
 * 2.初始化两个空链表ha,hb
 * 3.遍历链表h
 */
public class LinkListDemo {
    private NodeSql head;//定义一个头结点也就是哨兵h
    private int size = 0;


    public LinkListDemo() {
        head = new NodeSql();//定义头结点
        head.next = head;//循环链表
    }

    /**
     * 进行头插入数据元素
     * 思路:把元素依次插入
     */
    public void addHead(Object elem) {
        if (elem != null) {
            NodeSql p;
            p = head;
            int j = 0;
            while (p.next != head && j < size) {
                p = p.next;
            }
            NodeSql s = new NodeSql(elem);
            //把s插入链表中
            s.next = head;
            p.next = s;
            size++;


        }
    }

    public void getAllElem() {
        NodeSql p;
        p = head.next;
        while (p != null) {
            System.out.print(p.data+" ");
            if (p.next == head) {
                p = null;
            } else {
                p = p.next;
            }

        }
    }

    /**
     *遍历当前链表,负数插入到hb,正数插入到ha
     * @param ha
     * @param hb
     */
    public void analyse(LinkListDemo ha,LinkListDemo hb){
        NodeSql p;
        p=head;
        while (p!=null){
            Object obj=p.data;
            if(obj instanceof  Integer){
                Integer tb= (Integer) obj;
                if(tb<0){
                    hb.addHead(tb);
                }else {
                    ha.addHead(tb);
                }
            }

            if (p.next==head){
                p=null;
            }else {
                p=p.next;
            }
        }

    }


}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值