JAVA day16、17 数据结构(栈、队列、数组、链表、红黑树)

一、什么叫数据结构?

数据结构是相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合。“结构”就是指数据元素之间存在的关系,分为逻辑结构和存储结构。

程序 = 数据结构 + 算法 

1、数据的逻辑结构

指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后间关系,而与他们在计算机中的存储位置无关。逻辑结构包括:

1.集合:数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系; 

2.线性结构:数据结构中的元素存在一对一的相互关系;(表) 

3.树形结构:数据结构中的元素存在一对多的相互关系;(倒悬树)

4.图形结构:数据结构中的元素存在多对多的相互关系。(地图)

2、数据的物理(内存)结构

指数据的逻辑在计算机存储空间的存放形式。

1.顺序结构(数组)

数组存储在连续的内存空间中,用数据元素的相对位置来表示关系。

2.链式结构(链表)

数据元素存储在彼此独立的内存空间中,每个独立的元素也叫做节点,每个数据元素中增加一个数据项用来存储其他元素的地址,用来表示元素之间的关系。

逻辑结构与物理结构的关系

         表   -->   顺序 、链式
         树   -->   顺序 、链式
         图   -->   顺序 + 链式

A.顺序结构实现

  • 底层实现:
    数组 Array ,是有序的元素序列,数组是在内存中开辟⼀段连续的空间,并在此空间存放元素。
  • 特点:1.按照顺序排列,每个元素都带有标号;2.除了有标号是连续的,内存中的物理空间也是连续的。
  • 优缺点:查询快,增删慢。
package day16_17data.Arr;


import java.util.Arrays;

/*
数据结构分为逻辑结构 和 存储(物理)结构
逻辑结构:
	  集合: 数据元素同属于一个集体,但是元素之间没有任何关系
	  线性结构: 数据元素之间存在一对一的关系(表)
	  树型结构: 数据元素之间存在一对多的关系(倒悬树)
	  图型结构: 数据元素之间存在多对多的关系(地图)

	物理(内存)结构:
	  顺序结构(数组)
	  	数组存储在连续的内存空间中,用数据元素的相对位置来表示关系.


	  链式结构(链表)
	  	数据元素存储在彼此独立的内存空间中,每个独立的元素也叫做节点,
	  	每个数据元素中增加一个数据项用来存储其他元素的地址,
	  	用来表示元素之间的关系.

	 逻辑结构和物理结构之间的关系:
	 	表   ->   顺序  链式
	 	树   ->   顺序  链式
	 	图   ->   顺序 + 链式
------------------------------------------------------------------------------
顺序结构:
	底层实现:数组
    目的: 为了让外部类看起来是一个可变长的数组

  1.向集合(this)中末尾添加元素
  2.向集合index的位置中插入obj元素
  3.删除指定位置(index)上的元素,并且返回删除的元素
  4.删除第一个指定元素(obj)
  5.替换指定位置上的元素,替换成obj,并且返回被替换的元素
  6.从集合中获得指定位置(index)的元素

  7.获得集合中的元素个数
  8.判断集合中是否存在指定元素obj
  9.判断集合是否为空:没有有效元素是空
  10.打印出在集合中的有效元素
 */
public class ArraysDemo01 {
    //成员变量
    Object[] arr = new Object[5];
    int size = 0;//表示有效元素个数


    //1.向集合(this)中末尾添加元素
    public void add(Object obj){
        //判断有效个数是否超过数组的长度
        if(size == arr.length){
            //数组扩容
            arr = Arrays.copyOf(arr,arr.length + 5);
        }
        //arr[arr.length-1] = obj;//添加到末尾
        //将元素添加到数组中
        arr[size] = obj;
        //数组个数+1
        size++;
    }

    //2.向集合index的位置中插入obj元素
    public void add(int index,Object obj){
        //解决越界问题
        if(index < 0 || index > size - 1){
            return;
        }
        //数组扩容
        if(size == arr.length){
            arr = Arrays.copyOf(arr,arr.length + 5);
        }
        for (int i = size - 1 ; i >= index ; i--) {
            arr[i] = arr[i-1];
        }
        arr[index] = obj;
        size++;
    }

    //3.删除指定位置(index)上的元素,并且返回删除的元素
    public Object remove(int index){
        //解决越界问题
        if(index > size - 1 || index < 0){
            return null;
        }
        //将被删除的元素保存下来
        Object o = arr[index];
        for (int i = index + 1; i < size; i++) {
            arr[i - 1] = arr[i];
        }
        size--;
        return o;
    }

    //4.删除第一个指定元素(obj)
    public void remove(Object obj){
        for (int i = 0; i < size; i++) {
            if (obj.equals(arr[i])){
                //如果相同
                remove(i);
                break;
            }
        }
    }

    //5.替换指定位置上的元素,替换成obj,并且返回被替换的元素
    public Object set(int index,Object obj){
        //解决越界问题
        if(index > size - 1 || index < 0){
            return null;
        }
        //保存index位置上的元素
        Object o = arr[index];
        //替换元素
        arr[index] = obj;
        return o;
    }

    //6.从集合中获得指定位置(index)的元素
    public Object get(int index){
        return arr[index];
    }

    // 7.获得集合中的元素个数 -> size
    public int size(){
        return size;
    }

    // 8.判断集合中是否存在指定元素obj
    public boolean contains(Object obj){
        for (int i = 0; i < size; i++) {
            if(obj.equals(arr[i])){
                return true;
            }
        }
        return false;
    }

    //9.判断集合是否为空:没有有效元素是空
    public boolean isEmpty(){
        return size == 0;
    }


    //10.打印出在集合中的有效元素
    public String toString(){
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < size; i++) {
            sb.append(arr[i] + ",");
        }
        //去除最后一位
        //sb.deleteCharAt(sb.length()-1);
        //sb.append("]");
        sb.replace(sb.length()-1,sb.length(),"]");
        //字符串的拼接
        return sb.toString();
    }
}



public class MainArr01 {
    public static void main(String[] args) {
        ArraysDemo01 list = new ArraysDemo01();
        list.add(25);
        list.add(8);
        System.out.println(list);

        list.add(1,"hi");//指定位置+
        System.out.println(list);

        list.remove(0);
        list.contains("hi");

        System.out.println(list.toString());//[hi,null]


    }
}

B.链式结构实现

  • 底层实现:节点(data数据 + next下一个节点的引用)
    linked list ,由⼀系列结点 node (链表中每⼀个元素称为结点)组成,结点可以在运⾏时i 动态⽣成。每个结点包括两个部分:⼀个是存储数据元素的数据域,另⼀个是存储下⼀个结点地址的指针域。
  • 特点:1.链表是内存中固定顺序,但是他的物理空间不连续
               2.没有下标,并且从头节点开始
               3.所有节点的访问,都必须通过头节点
               4.head(头节点): 只存next,不存data
               5.head.next = null -> 空链表
  • 三种类型的链表:1.单向链表:只有头节点,只能从头节点出发;2.双向链表:有头节点和尾结点,可以从头或者尾出发;3.循环链表:尾节点指向头节点,可以从任意位置开始。
  • 优缺点:查询慢,插入、删除效率高;

public class SingleLinkDemo {
    //链表中只存在节点
    private Node head = new Node();//初始化Node
    public int size = 0;//表示有效元素个数

    //成员内部类
    public class Node{
        public Object data;//存储数据
        public Node next;//下一个节点的引用
    }

    //获取index之前的一个节点
    public Node prevNode(int index){
        /*if (index > size - 1 || index < 0){
            return null;
        }*/
        //获取头节点
        Node node = head;
        //找到index下标前的一个节点位置
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
        return node;
    }



    //1.向集合(this)中末尾添加元素
    public void add(Object obj){
        //获取头节点
        Node node = head;
        //判断下一个节点是否为空
        while(node.next != null){
            //将节点往下移动一位
            node = node.next;
        }
        //新建节点
        Node newNode = new Node();
        //存储新节点的数据
        newNode.data = obj;
        //上一个节点指向新的节点
        node.next = newNode;
        //节点个数+1
        size++;
    }

    //2.向集合index的位置中插入obj元素
    public void add(int index,Object obj){
        //获取头节点
        Node node = head;
        //找到index下标前的一个节点位置
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
        //Node node = prevNode(index);
        //插入新的节点
        Node newNode = new Node();
        newNode.data = obj;
        //插入新节点:
        //a.新节点的下一个节点是原来的节点
        newNode.next = node.next;
        //b.node的下一个节点等于新的节点
        node.next = newNode;
        //元素个数+1
        size++;
    }

    //3.删除指定位置(index)上的元素,并且返回删除的元素
    public Object remove(int index){
        //找到index之前的一个节点
        Node node = prevNode(index);
        //保存index位置上的数据
        Object o = node.next.data;
        node.next = node.next.next;
        size--;
        return o;
    }

    // 4.删除第一个指定元素(obj)
    public void remove(Object obj){
        Node node = head;
        //假设下标
        int index = -1;
        //方式一:
        while(node.next != null){
            index++;
            node = node.next;
            //判断node节点上的数据和 obj是否相同
            if(node.data.equals(obj)){
                remove(index);
                break;
            }
        }

        //方式二:
        /*while(node.next.data != obj){
            node = node.next;
        }
        node.next = node.next.next;*/

        //方式三:
        /*for (;node.next.data != obj; index++) {
            node = node.next;
        }
        remove(index);*/

        //方式四:
        /*for (int i = 0; i < size; i++) {
            if(node.next.data != obj){
                node = node.next;
            }
        }
        size--;*/

    }

    //5.替换指定位置上的元素,替换成obj,并且返回被替换的元素
    public Object set(int index,Object obj){
        Node node = prevNode(index + 1);
        Object o = node.data;
        node.data = obj;
        return o;

    }
    // 6.从集合中获得指定位置(index)的元素
    public Object get(int index){
        //获取index当前节点位置
        Node node = prevNode(index + 1);
        return node.data;
    }

    //  7.获得集合中的元素个数
    public int size(){
        //会产生栈溢出
        //return size();
        return size;
    }
    //  8.判断集合中是否存在指定元素obj
    public boolean contains(Object obj){
        Node node = head;
        while (node.next != null){
            node = node.next;
            if(node.data.equals(obj)){
                return true;
            }
        }
        return false;
    }
    //  9.判断集合是否为空:没有有效元素是空
    public boolean isEmpty(){
        return size == 0;
    }


    public String toString(){
        StringBuilder sb = new StringBuilder("[");
        //获取链表中的元素
        Node node = head;
        while (node.next != null){
            node = node.next;
            sb.append(node.data + ",");
        }
        return sb.toString();
    }

}

public class MainLink {
    public static void main(String[] args) {
        SingleLinkDemo slink = new SingleLinkDemo();
        slink.add(1);
        slink.add(2);
        slink.add(3);
        slink.add(4);
        slink.add(2,10);
        System.out.println(slink);

        slink.isEmpty();
        slink.get(1);
        slink.contains(2);
        slink.remove(0);

    }
}

二、常见的数据结构

数据存储的常⽤结构有:栈、队列、数组、链表和红⿊树。

1、栈(Stack)

stack ,⼜称堆栈,它是运算受限的线性表,其限制是仅允许在标的⼀端进⾏插⼊和删除操作,不允许在其他任何位置进⾏添加、查找、删除等操作。

特点:

  • 先进后出(即,存进去的元素,要在后它后⾯的元素依次取出后,才能取出该元素)。
  • 栈的⼊口、出⼝的都是栈的顶端位置。
压栈:
就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底⽅向移动⼀ 个位置。
弹栈
就是取元素。 即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶⽅向移动⼀个 位置。

自定义代码实现栈 (数组)

/*
栈
用数组实现
*/
public class ArraysDemo01 <T>{
    private Object[] data;//存储数据的数组
    private int size = 0;//表示元素有效个数

    ArraysDemo01(){
        data = new Object[10];//初始化数组长度
    }
    ArraysDemo01(int cap){
        data = new Object[cap];//初始化数组长度
    }

    //压栈
    public void push(T t){
        //数组扩容
        if(size == data.length){
            data = Arrays.copyOf(data,data.length + 5);
        }
        /* 将obj元素添加到数组中
        data[size] = obj;
        //有效个数+1
        size++;*/
        //等同于以上的写法
        data[size++] = t;
    }

    //弹栈
    public T pop(){
        //有效个数的下标 size-1
        /*T t = (T)data[size - 1];
        size--;*/
        return (T)data[--size];//弹出去一个 -1
    }


    //打印出在集合中的有效元素
    public String toString(){
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < size; i++) {
            sb.append(data[i] + ",");
        }
        //去除最后一位
        //sb.deleteCharAt(sb.length()-1);
        //sb.append("]");
        sb.replace(sb.length()-1,sb.length(),"]");
        //字符串的拼接
        return sb.toString();
    }


}
//数组实现栈测试
public class Main01 {
    public static void main(String[] args) {
        ArraysDemo01 arrStack = new ArraysDemo01();
        arrStack.push(22);
        arrStack.push("xixi");
        System.out.println(arrStack);//[22,xixi]

        arrStack.pop();
        System.out.println(arrStack);//[22]
    }
}

自定义代码实现栈 (链表)

/*
    链表方式实现栈
 */
public class LinkDemo02 {
    //头节点
    private Node head = new Node();

    //节点类
    public class Node{
        public Object data;
        public Node next;
    }

    //压栈
    public void push(Object obj){
        //创建新的节点
        Node newNode = new Node();
        newNode.data = obj;
        while (head.next != null){
            newNode.next = head.next;
        }
        //让头节点的next指向新加入的节点
        head.next = newNode;
    }

    //弹栈
    public Object pop(){
        //保存被弹出的数据
        Node node = head.next;
        //让头节点指向下下节点
        head.next = head.next.next;
        //获取被弹出的节点的数据
        return node.data;
    }


  

}
//链表实现栈测试
public class Main02 {
    public static void main(String[] args) {
        LinkDemo02 linkStack = new LinkDemo02();
        linkStack.push("xii");
        linkStack.push(5555);
        System.out.println(linkStack);

        linkStack.pop();
        System.out.println(linkStack);

    }
}

2.队列(Queue)

队列 queue ,简称队,它同堆栈⼀样,也是⼀种运算受限的线性表,其限制是仅允许在表
的⼀端进⾏插⼊,⽽在表的另⼀端进⾏删除。
特点:
  • 先进先出(即,存进去的元素,要在后它前⾯的元素依次取出后,才能取出该元素)。
  • 队列的⼊⼝、出⼝各占⼀侧。 

自定义代码实现队列(链表)也可以通过数组实现

  /*
    链表实现队列
 */


public class LinkQueueDemo {

        //单链表作为成员变量使用
        private SingleLinkDemo link = new SingleLinkDemo();

        //入队:
        public void push(Object obj){
            link.add(0,obj);
        }

        //出列:
        public Object pop(){
            //获取元素
            Object o = link.get(link.size() - 1);
            //删除元素
            link.remove(link.size() - 1);
            return o;
        }

        @Override
        public String toString() {
            return link.toString();
        }


}
public class MainQueue {
    public static void main(String[] args) {
        LinkQueueDemo q = new LinkQueueDemo();
        q.push(55);
        q.push("55555");;
        System.out.println(q);

        q.pop();
        System.out.println(q);
    }
}

 数组跟链表看上面顺序结构跟链式结构的解释。

3.二叉树:红黑树

⼆叉树 binary tree ,是每个结点不超过 2 的有序 树( tree
⼆叉树是每个节点最多有两个⼦树的树结构。顶上的叫根结点,两边被称作 左⼦树 右⼦树
红黑数的本身就是一颗二叉查找树。
红⿊树的约束:
1. 节点可以是红⾊的或者⿊⾊的
2. 根节点是⿊⾊的 3. 叶⼦节点(特指空节点)是⿊⾊的
4. 每个红⾊节点的⼦节点都是⿊⾊的
5. 任何⼀个节点到其每⼀个叶⼦节点的所有路径上⿊⾊节点数相同
红⿊树的特点:
速度特别快,趋近平衡树,查找叶⼦元素最少和最多次数不多于⼆倍
关于树的详细了解 见java day18-19的学习笔记

三、分类

数据结构有很多种,一般来说,按照数据的逻辑结构对其进行简单的分类,包括线性结构和非线性结构两类。

1.线性结构

简单地说,线性结构就是表中各个结点具有线性关系。如果从数据结构的语言来描述,线性结构应该包括如下几点:

1、线性结构是非空集。 

2、线性结构有且仅有一个开始结点和一个终端结点。 

3、线性结构所有结点都最多只有一个直接前趋结点和一个直接后继结点。

线性表就是典型的线性结构,还有栈、队列和串等都属于线性结构。 

2.非线性结构

简单地说,非线性结构就是表中各个结点之间具有多个对应关系。如果从数据结构的语言来描述,非线性结构应该包括如下几点:

1、非线性结构是非空集。

2、非线性结构的一个结点可能有多个直接前趋结点和多个直接后继结点。

在实际应用中,数组、广义表、树结构和图结构等数据结构都属于非线性结构。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值