数据结构小结 1

## 对几个常见的线性数据结构做一个简单的分析和自定义的实现

1.顺序表

按照顺序存储方式存储的线性表,在计算机的一组连续的存储单元中,因此在查找的时候由于地址连续性,cpu寻址时是按照顺序往下,这样在寻找时会非常迅速,这就导致了顺序存储方式的查找非常高效。同时,由于地址连续性,在中间删除或者增加一个节点时,会影响到后面节点在物理内存中的地址都要往前或往后移动,因此当插入或删除时效率较低,而且随操作影响的节点的数量越多,效率越低。这里用Java实现一个自定义的顺序表


package baseDataStructure;

import java.util.Scanner;

/**
 * 顺序表结构
 * 即按照顺序存储方式的线性表类似数组
 * 每个节点的长度相同,在物理内存中连续存在,一个整的空间存放顺序表
 * 便于查询,增删效率低
 */
public class SequentialList {

    static final int MAXLEN = 100;      //定义顺序表的最大长度
    DataList[] listData = new DataList[MAXLEN+1]; //保存顺序表的结构数组
    int ListLen; //顺序表已经存的节点的数量

    /**
     * 初始化为空表,将位置指向0,这样在写入时如果有数据会直接覆盖,而不用先将数据清空
     * @param sequentialList
     */
    void init(SequentialList sequentialList){
        sequentialList.ListLen=0;
    }

    /**
     * 返回顺序表的节点数量
     * @param sequentialList
     * @return
     */
    int getListLength(SequentialList sequentialList){
        return sequentialList.ListLen;
    }

    /**
     * 插入节点
     * @param sequentialList 顺序表
     * @param n 插入的位置
     * @param dataList 节点对象
     * @return 1表示成功,0表示失败
     */
    int insertSequentialList(SequentialList sequentialList,int n,DataList dataList){
        int i;
        if (sequentialList.ListLen >= MAXLEN){
            System.out.println("顺序表已满,无法插入节点");
            return 0;
        }else if (n<1||n>sequentialList.ListLen-1){
            System.out.println("位置有误:当前指定位置没有存储元素,无法插入");
            return 0;
        }
        for (i = sequentialList.ListLen; i >=n ; i--) {
            //将插入位置后面的所有元素下标向后移动一位,空出插入位置节点
            sequentialList.listData[i+1] = sequentialList.listData[i];
        }
        sequentialList.listData[n]=dataList;
        sequentialList.ListLen++;
        return 1;
    }

    /**
     * 在顺序表末尾加上元素
     * @param sequentialList
     * @param dataList
     * @return 1表示成功,0表示失败
     */
    int addSequentialList(SequentialList sequentialList,DataList dataList){
        if (sequentialList.ListLen>=MAXLEN){
            System.out.println("顺序表已满,无法插入节点");
            return 0;
        }
        sequentialList.listData[++sequentialList.ListLen]=dataList;
        return 1;
    }

    /**
     * 删除节点位置元素
     * @param sequentialList
     * @param n
     * @return 1表示成功,0表示失败
     */
    int delSequentialList(SequentialList sequentialList,int n){

        if (n<1||n>sequentialList.ListLen+1){
            System.out.println("位置错误:删除节点的位置没有存有元素,无法删除");
            return 0;
        }
        for (int i = n; i<sequentialList.ListLen ; i++) {
            //将所有n位置后面的元素都向前挪动一位,然后删除最后一个
            sequentialList.listData[i]=sequentialList.listData[i+1];
        }
        sequentialList.ListLen--;
        return 1;
    }

    /**
     * 通过下标位置查找元素
     * @param sequentialList
     * @param n
     * @return
     */
    DataList findSequentialList(SequentialList sequentialList,int n){
        if (n<1||n>sequentialList.ListLen+1){
            System.out.println("位置错误:查找节点的位置没有存有元素,无法查询");
            return null;
        }else {
            return sequentialList.listData[n];
        }
    }

    /**
     * 通过关键词(节点类里定义好的key)查询节点的位置返回下标
     * @param sequentialList
     * @param key
     * @return 成功返回下标位置,失败返回0
     */
    int  findSequentialListByKey(SequentialList sequentialList,String key){
        for (int i = 1; i <=sequentialList.ListLen ; i++) {
            //compareTo 相等返回0,小于返回-1,大于返回1
            if (sequentialList.listData[i].key.compareTo(key)==0){
              return i;
            }
        }
        return 0;
    }

    /**
     * 显示当前顺序表所有节点内容
     * @param sequentialList
     */
    void showAllSequentialList(SequentialList sequentialList){
        for (int i = 1; i <=sequentialList.ListLen ; i++) {
            System.out.println("第 "+i+" 个元素节点内容是\n"+sequentialList.listData[i].toString());
        }
    }


    /**
     * 使用实例
     * @param args
     */
    public static void main(String[] args) {

        SequentialList sequentialList = new SequentialList(); //定义顺序表
        DataList dataList;  //定义节点引用的变量类型
        String key;  //定义查询时的关键词

        sequentialList.init(sequentialList); //初始化表

        Scanner sc = new Scanner(System.in);

        //循环添加节点内容
        do {
            System.out.println("请输入 关键词 姓名 年龄");
            DataList tmpData = new DataList();
            tmpData.key = sc.next();
            tmpData.name = sc.next();
            tmpData.age = sc.nextInt();

            if (tmpData.age!=0){
                if (sequentialList.addSequentialList(sequentialList,tmpData)==0){
                    break;
                }
            }else {
                break;
            }
        }while (true);

        System.out.println("显示顺序表中的节点顺序");
        sequentialList.showAllSequentialList(sequentialList);

        System.out.println("要取出的节点号码为:");
        int i = sc.nextInt();
        dataList = sequentialList.findSequentialList(sequentialList,i);
        if (dataList!=null){
            System.out.println("第"+i+"个元素内容是"+dataList.toString());
        }

        System.out.println("输入关键词");
        key = sc.next();
        i = sequentialList.findSequentialListByKey(sequentialList,key);
        dataList = sequentialList.findSequentialList(sequentialList,i);
        if (dataList!=null){
            System.out.println("关键词为"+key +"的元素内容是"+dataList.toString());
        }
    }

}


/**
 * 定义节点元素内容类,可以是一个任意的实体类
 * 类似数组就是一个int 下标和一个Object value的实体类
 */
class DataList{
    String key;  //节点关键词
    String name;
    int age;

    @Override
    public String toString() {
        return "DataList{" +
                "key='" + key + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2.链表

链表结构是一种动态存储分配的结构形式,一般链表有两个部分,数据部分和地址部分。数据部分用于存储节点内的数据模型等,地址部分则保存下一个节点的地址。在定义链表时,需要定义一个头引用变量(head),这个head指向链表的第一个节点,第一个节点的地址部分指向第二个节点,直到最后一个节点的地址部分指向null 则表示链表完成。因此在物理内存中基本上都是非连续存在的,比如一个8G的内存区,第一个节点在3G的位置,第二个在7G的位置,而CPU需要挨个寻址,会导致查询的效率极低。(一般情况下不会出现,因为系统会给每个进程分配一定的空间 32位系统好像是在4G,前2G作为进程私有空间,后2G作为公用空间,同时在堆空间内,因此一般不会相距太远)但是在增删时只影响上一个节点的地址部分,而不用其他的节点内容,因此效率非常高。

package baseDataStructure;

import java.util.Scanner;

/**
 * 链表自定义
 * 每个节点包括两个部分,数据部分和地址部分,地址部分指向下一个节点的地址
 * 如果是双向链表,地址部分还包含指向上一个节点的地址
 * 链表不同于顺序表,在物理内存中不连续
 * 便于增删而不便于查找
 */
public class LinkList {

    LinkListData nodeData = new LinkListData();
    LinkList nextNode;
    /**
     * 在链表末尾添加节点
     * @param linkList  被添加的链表对象
     * @param data 需要添加的节点对象
     * @return 返回添加完成的节点
     */
    LinkList addLinkListAtEnd(LinkList linkList,LinkListData data){
        //node 表示需要添加的末尾对象
        // headTemp用于存储临时赋值变量
        LinkList node,headTemp;

        node = new LinkList(); //申请保存节点数据的内存空间
        if (node==null){
            System.out.println("内存申请失败,节点为空");
            return null;
        }else {
            node.nodeData = data; //将需要添加的数据对象赋值给申请的节点
            node.nextNode = null; //将引用节点设置为空,表示这是末尾节点

            if (linkList==null){ //如果当前链表还没有存储对象
                linkList = node ;
                return linkList;
            }else {
                headTemp = linkList; //将当前链表赋值给headTemp
                while (headTemp.nextNode!=null){ //查找链表尾节点
                    headTemp=headTemp.nextNode;
                }
                headTemp.nextNode = node;
                return linkList;
            }
        }
    }


    /**
     * 在链表头部添加节点
     * @param linkList
     * @param data
     * @return
     */
    LinkList addLinkListAtHead(LinkList linkList,LinkListData data){
        LinkList node = new LinkList();
        if (node==null){
            System.out.println("内存申请失败,节点为空");
            return null;
        }else {
            node.nodeData = data;
            node.nextNode = linkList;
            linkList = node;
            return linkList;
        }

    }


    /**
     * 通过关键词查询链表
     * @param linkList
     * @param key
     * @return
     */
    LinkList findLinkListByKey(LinkList linkList,String key){
        LinkList tmpLinkList = linkList; //保存头节点引用
        while (tmpLinkList!=null){
            if (tmpLinkList.nodeData.key.compareTo(key)==0){
                return tmpLinkList;
            }else {
                tmpLinkList = tmpLinkList.nextNode;
            }
        }
        return null;
    }

    /**
     * 插入节点,找到关键节点并排其后面
     * @param linkList
     * @param findKey 插入节点需要找到对应位置,链表查找位置的条件,
     * @param data
     * @return
     */
    LinkList insertLinkList(LinkList linkList,String findKey,LinkListData data){
        LinkList node,nodeTemp;
        node = new LinkList();
        if (node==null){
            System.out.println("内存申请失败,节点为空");
            return null;
        }else {
            node.nodeData = data;
            nodeTemp = findLinkListByKey(linkList,findKey);
            if (nodeTemp!=null){
                node.nextNode = nodeTemp.nextNode;
                nodeTemp.nextNode = node;
            }else {
                System.out.println("没有找到插入的位置");
            }
            return linkList;
        }

    }

    /**
     * 删除节点
     * @param linkList
     * @param findKey
     * @return 成功返回1 失败返回0
     */
    int delLinkList(LinkList linkList,String findKey){
        LinkList node=linkList,nodeTemp=linkList;
        while (nodeTemp!=null){
            /**
             * 具体操作如下
             * 假如链表为  1——> 2 ——> 3 ——> 4 需要删除3
             * node 第一轮为 1——> 2 ——> 3 ——> 4
             * nodeTemp 第一轮为  2 ——> 3 ——> 4
             * node 第二轮为  2 ——> 3 ——> 4
             * nodeTemp 第二轮为   3 ——> 4
             * 第三轮时 nodeTemp 的 头节点关键词为3 执行if 操作
             * 此时  node.nextNode ==> 3 ,nodeTemp.nextNode ==> 4
             * 将 node.nextNode = nodeTemp.nextNode;
             *那么得到的 node 的链表全结果为  1 ——> 2 ——> 3 ——> 4
             *
             */

            if (nodeTemp.nodeData.key.compareTo(findKey)==0){
                node.nextNode = nodeTemp.nextNode;
                nodeTemp = null;  //释放内存
                return 1;
            }else {
                node = nodeTemp;
                nodeTemp=nodeTemp.nextNode;
            }
        }
        return 0;

    }

    /**
     * 得到链表长度
     * @param linkList
     * @return
     */
    int getLengthLinkList(LinkList linkList){
        LinkList tmpLinkList = linkList;
        int l = 0;
        while (tmpLinkList!=null){
            l++;
            tmpLinkList=tmpLinkList.nextNode;
        }
        return l;
    }

    /**
     * 显示所有节点内容
     * @param linkList
     */
    void showAllLinkListNodes(LinkList linkList){
        int i= 0;
        LinkList tmpLinkList = linkList;
        LinkListData data;
        while (tmpLinkList!=null){
            i++;
            data = tmpLinkList.nodeData;
            System.out.println("第 "+i+" 个节点内容为 "+data.toString());
            tmpLinkList = tmpLinkList.nextNode;
        }
    }


    public static void main(String[] args) {
        LinkList linkList ;
        LinkList headNode = null;
        LinkList LINK = new LinkList();
        String key;
        Scanner sc = new Scanner(System.in);
        System.out.println("输入数据,关键词,姓名,年龄");
        do {
            LinkListData data = new LinkListData();
            data.key = sc.next();
            if (data.key.equals("0")){
                break;
            }else {
                data.name = sc.next();
                data.age = sc.nextInt();
                headNode = LINK.addLinkListAtEnd(headNode,data); //向headNode中添加节点
            }
        }while (true);

        //显示
        LINK.showAllLinkListNodes(headNode);

        System.out.println("删除节点关键词");
        key = sc.next();
        LINK.delLinkList(headNode,key);
        LINK.showAllLinkListNodes(headNode);

        System.out.println("查询关键词");
        key = sc.next();
        linkList = LINK.findLinkListByKey(headNode,key);
        if (linkList!=null){
            System.out.println(key+" 查询结果为:"+linkList.nodeData.toString());
        }else {
            System.out.println(key+" 无此结果");
        }
    }
}


class LinkListData{
    String key;
    String name;
    int age;
    @Override
    public String toString() {
        return "LinkListData{" +
                "key='" + key + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

3.栈

从数据的运算分类上看为栈,先进后出。从数据的逻辑结构看是一种线性结构。这里主要说的是顺序栈,即在物理内存中连续存在。链式栈是链式结构存储在内存中的。栈的先进后出机制即先入栈的必须在其他节点弹出后才能取出,在栈中,只能访问当前栈顶的元素,当弹出后才能访问下一个。一般有两个操作,入栈和出栈。
入栈即将数据保存到栈顶,出栈即修改栈顶的引用使其指向下一个元素。
这里可以想象为一个特殊的数组形式,里面存放的是节点保存的数据类型可以是一个封装好的实体类等。而这个数组在添加时按照一定顺序,下标为0的保存栈底的节点,以此类推,当有n个节点保存后,栈顶的指向为n-1。在访问时,首先只能访问第n-1个,访问后将指向改为n-2,以此类推访问至最后一个节点。

 package baseDataStructure;

import java.util.Scanner;

/**
 * 自定义顺序栈的实现
 * 栈是一个先入后出的线性表
 * 一般拥有入栈(push) 出栈(pop)两个操作
 */
public class SequenceStack {
    static final int MAXLEN = 50;
    SequenceStackData[] data = new SequenceStackData[MAXLEN+1];
    int top;


    /**
     * 栈的初始化
     * @return
     */
    SequenceStack init(){
        SequenceStack stack = new SequenceStack();
        if (stack!=null){
            stack.top = 0;  //申请成功后申请栈顶为0
            return stack;
        }
        return null;
    }

    /**
     * 判断是否为空栈,当栈顶top引用为0时,栈为空
     * @param stack
     * @return 为空返回true
     */
    boolean stackIsEmpty(SequenceStack stack){
        return stack.top==0;
    }

    /**
     * 判断栈是否已经满了,当栈顶top达到最大值时,栈满
     * @param stack
     * @return 满了返回true
     */
    boolean stackIsFull(SequenceStack stack){
        if (stack.top==MAXLEN){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 设置栈为空,将top引用置为0
     * @param stack
     */
    void stackClear(SequenceStack stack){
        stack.top = 0;
    }

    /**
     * 释放栈空间
     * @param stack
     */
    void stackFree(SequenceStack stack){
        if (stack!=null){
            stack = null;
        }
    }

    /**
     * 入栈操作
     * @param stack
     * @param data
     * @return
     */
    int stackPush(SequenceStack stack,SequenceStackData data){
        if (stack.top>=MAXLEN){
            System.out.println("栈已满,无法入栈");
            return 0;
        }else {

            stack.data[++stack.top]=data; //元素入栈
            return 1;
        } 
    }

    /**
     * 出栈操作
     * @param stack
     * @return
     */
    SequenceStackData stackPop(SequenceStack stack){
        if (stack.top==0){
            System.out.println("栈内没有元素内容");
            return null;
        }else {
           return  stack.data[stack.top--];
        }
    }

    /**
     * 读取节点数据
     * @param stack
     * @return
     */
    SequenceStackData getStackData(SequenceStack stack){
        if (stack.top==0){
            System.out.println("没有数据在栈内");
            System.exit(0);
        }
        return stack.data[stack.top];
    }


    /**
     * 测试实例
     * @param args
     */
    public static void main(String[] args) {
        SequenceStack stack = new SequenceStack();
        SequenceStackData data = new SequenceStackData();

        stack = stack.init(); //初始化

        Scanner sc = new Scanner(System.in);

        System.out.println("入栈,输入姓名,年龄");
        do {
            SequenceStackData tmpData = new SequenceStackData();
            tmpData.name = sc.next();
            if (tmpData.name.equals("0")){
                break;
            }else {
                tmpData.age = sc.nextInt();
                stack.stackPush(stack,tmpData);
            }
        }while (true);

        String temp = "1";
        System.out.println("出栈:任意非0键进行出栈操作");
        temp = sc.next();
        while (!temp.equals("0")){
            data = stack.stackPop(stack);
            System.out.println(data.toString());
            temp = sc.next();
        }

        System.out.println("释放空间");
        stack.stackFree(stack);
    }



}

/**
 * 简单定义栈内节点数据封装类
 */
class SequenceStackData{
    String name;
    int age;

    @Override
    public String toString() {
        return "SequenceStackData{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

4.队列

队列从数据的逻辑结构上讲也是一种线性结构,这里也主要讲顺序队列结构,还有一种链式队列结构。队列是先进先出的规则。一般有两种操作。入队操作,将节点添加到队尾。出队操作:从队头取出节点并删除,使下一个节点成为队头。

 package baseDataStructure;

import java.util.Scanner;

/**
 * 自定义顺序队列结构
 * 先进先出
 *
 */
public class SequentialQueue {

    static final int QUEUELEN = 15;
    QueueData[] data = new QueueData[QUEUELEN];
    int head;
    int tail; //定义队头和队尾

    SequentialQueue queueInit(){
        SequentialQueue queue; //申请内存
        if ((queue=new SequentialQueue())!=null){
            queue.head = 0;  //设置队头为0
            queue.tail = 0;  //设置队尾为0
            return queue;
        }else {
            return null;
        }
    }

    /**
     * 判断队列是否为空
     * @param queue
     * @return 返回0为空
     */
    int queueIsEmpty(SequentialQueue queue){
        if (queue.head==queue.tail){
            return 0;
        }else {
            return 1;
        }
    }

    /**
     * 判断队列是否已满
     * @param queue
     * @return 返回0为满
     */
    int queueIsFull(SequentialQueue queue){
        if (queue.tail>=QUEUELEN){
            return 0;
        }else {
            return 1;
        }
    }

    /**
     * 清空队列
     * @param queue
     */
    void queueSetClear(SequentialQueue queue){
        queue.head = 0;
        queue.tail = 0;
    }

    /**
     * 释放内存
     * @param queue
     */
    void queueSetFull(SequentialQueue queue){
        if (queue!=null){
            queue = null;
        }
    }

    /**
     * 入队操作
     * @param queue
     * @param data
     * @return 返回1成功 0失败
     */
    int queueIn(SequentialQueue queue,QueueData data){
        if (queue.tail==QUEUELEN){
            System.out.println("队列已满");
            return(0);
        }else {
            queue.data[queue.tail++]=data;
            return(1);
        }
    }

    /**
     * 出队操作
     * @param queue
     * @return
     */
    QueueData queueOut(SequentialQueue queue){

        if (queue.head==queue.tail){
            System.out.println("出对操作:队列已空");
            System.exit(0);
        }else {
            return queue.data[queue.head++];
        }
        return null;
    }

    /**
     * 读取队列数据操作
     * @param queue
     * @return
     */
    QueueData queueGet(SequentialQueue queue){
        if (queueIsEmpty(queue)==0){
            System.out.println("队列已空");
            return null;
        }else {
            return queue.data[queue.head];
        }
    }

    /**
     * 计算队列长度
     * @param queue
     * @return
     */
    int queueLen(SequentialQueue queue){
        return (queue.tail-queue.head);
    }

    /**
     * 测试实例
     * @param args
     */
    public static void main(String[] args) {
        SequentialQueue queue = new SequentialQueue();
        QueueData data;

        Scanner sc = new Scanner(System.in);
        queue = queue.queueInit();

        System.out.println("进行入队操作:输入姓名,年龄");
        do {
            QueueData queueData = new QueueData();
            queueData.name = sc.next();
            queueData.age = sc.nextInt();
            if (queueData.name.equals("0")){
                break;
            }else {
                queue.queueIn(queue,queueData);
            }
        }while (true);


        String temp = "1";
        System.out.println("出队列操作,输入任何非0键位");
        temp = sc.next();
        while (!temp.equals("0")){
            data = queue.queueOut(queue);

            System.out.println("队列内容为 "+data.toString());

            temp = sc.next();
        }

        System.out.println("释放内存");
        queue.queueSetFull(queue);

    }


}


/**
 * 简单队列节点对象数据封装
 */
class QueueData{
    String name;
    int age;

    @Override
    public String toString() {
        return "QueueData{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值