1 - 队列,链表,栈

1.逻辑结构和物理结构(存储结构)

1.1 逻辑结构

  • 集合结构(无关系
  • 线性结构(一对一
    • 代表:线性表,栈,队列, 串
  • 非线性结构
    • 树形结构(一对多
    • 图形结构(多对多

1.2 物理结构

  • 顺序存储结构
  • 链式存储结构
  • 索引
  • 散列表

2. 稀疏数组

2.1 介绍

  • 当一个数组中有效值少,0很多的时候,可以用稀疏数组存储。SparseArray
  • 如下所示,稀疏数组第0行,第一列6表示原数组有几行,第二列7表示原数组有几列,第三列8表示原数组有几个非0元素。稀疏数组第1行到最后一行的记录非0元素所在行列和它的值
  • 由此可见,稀疏数组的列必是3行数就是原始数组的非0元素个数+1(第0行)
    在这里插入图片描述

在这里插入图片描述

2.2 代码实现

package SparseArray;

public class Demo01 {

    public static void main(String[] args) {
        // 1. 创建原始数组,只要给需要的地方赋值非0,其余默认0
        int[][] o_array = new int[5][6];
        o_array[1][3] = 1;
        o_array[2][4] = 2;
        o_array[0][3] = 5;
        o_array[3][2] = 4;
        // 2. 获取原始数组的行列
        int lenx = o_array.length;
        int leny = o_array[0].length;

        // 3. 获取非0元素个数
        int non_zero = 0;
        for (int i =0; i<lenx;i++){
            for (int j=0;j<leny;j++){
                int temp = o_array[i][j];
                if(temp!=0){
                    non_zero++;
                }
            }
        }

        // 4. 创建稀疏数组
        int[][] sparse_array = new int[non_zero+1][3];
        sparse_array[0][0] = lenx;
        sparse_array[0][1] = leny;
        sparse_array[0][2] = non_zero;

        // 5. 遍历原始数组,把非0元素填入稀疏数组
        int x = 1;
        for (int i =0; i<lenx;i++){
            for (int j=0;j<leny;j++){
                int temp = o_array[i][j];
                if(temp!=0){
                    sparse_array[x][0] = i;
                    sparse_array[x][1] = j;
                    sparse_array[x][2] = temp;
                    x++;
                }
            }
        }

        // 6. 输出原始数组
        System.out.println("原始数组如下");
        for (int[] row: o_array){
            for (int data:row){
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }

        // 7. 输出稀疏数组
        System.out.println("稀疏数组如下");
        for (int[] row:sparse_array){
            for (int data:row){
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }

        // 8. 把稀疏数组回复成原始数组
        // 8.1 先获取稀疏数组第一行数据,用来创建原始数组大小
        int lenx2 = sparse_array[0][0];
        int leny2 = sparse_array[0][1];
        int[][] array2 = new int[lenx2][leny2];

        // 8.2 遍历稀疏数组,从第2行开始。把数据恢复
        for (int i=1; i< sparse_array.length; i++){
            array2[ sparse_array[i][0] ][ sparse_array[i][1] ] = sparse_array[i][2];
        }

        // 8.3 输出新的恢复后的原始数组
        System.out.println("恢复后的原始数组如下");
        for (int[] row: array2){
            for (int data:row){
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }
    }
}


3 队列

3.1 介绍

  • 队列是一个有序表,可以用数组或者链表实现
  • 先入先出

在这里插入图片描述

3.2 代码实现

  • rear和front默认-1(添加和取数据的时候,先+1再添加),如果默认=0,那么操作过程就不一样了(添加时候,先添加,然后rear+1,取数据时,先取然后front+1)。

  • 下面的队列是一次性的。因为队列填满值之后,就不能再加入了,因为填入的值没有出队列,只是返回了值而已。

public class Demo {
    public static void main(String[] args) {

        ArrayQueue a = new ArrayQueue(5);
        a.addQueue(1);
        a.addQueue(2);
        a.addQueue(3);
        a.addQueue(4);
        a.addQueue(5);
        a.showAllQueue();
        System.out.println();
        System.out.println(a.getQueue());
        System.out.println(a.getQueue());
        System.out.println(a.getQueue());
        System.out.println();
        a.showAllQueue();
        System.out.println();
        a.showQueue();
        a.addQueue(6);
        a.addQueue(7);
        a.addQueue(8);
        a.showAllQueue();

    }
}


class ArrayQueue{
    private int maxSize;    // 队列最大容量
    private int front;      // 队列头指针
    private int rear;       // 队列尾指针
    private int[] arr;      // 数组,用来模拟队列,保存数据

    // 构造器
    public ArrayQueue(int maxSize){
        this.maxSize = maxSize;
        arr = new int[maxSize];
        front = -1;
        rear = -1;
    }

    // 判断队列是否满了,如果尾指针==最大值-1(尾指针0开始),表示满了
    public boolean isFull(){
        return rear==maxSize-1;
    }

    // 判断队列是否为空,如果头尾指针相同,就是空了
    public boolean isEmpty(){
        return front==rear;
    }

    // 添加数据到队列
    public void addQueue(int n){
        if (isFull()) {
            System.out.println("队列已满,无法添加数据!");
            return;
        }
        // arr[++rear] = n;
        // 或者
        rear ++;
        arr[rear] = n;
    }

    // 获取队列数据,即从头部拿出数据
    public int getQueue(){
        if(isEmpty()){
            // 不能return,因为return的值可能是队列元素的值
            throw new RuntimeException("队列为空,不能取数据");
        }
        front++;
        return arr[front];
    }

    // 打印队列未取出的内容
    public void showQueue(){
        if (isEmpty()){
            System.out.println("队列为空,没有数据");
            return;
        }
        for (int i=front+1; i<arr.length; i++){
            System.out.printf("arr[%d] == %d\t", i, arr[i]);
        }
    }

    // 打印队列所有的内容,就算有取出的数据,那也只是返回了,而arr中并没有删除!
    public void showAllQueue(){
        if (isEmpty()){
            System.out.println("队列为空,没有数据");
            return;
        }
        for (int i=0; i<arr.length; i++){
            System.out.printf("arr[%d] == %d\t", i, arr[i]);
        }
    }

    // 获取头部数据
    public int getHead(){
        if (isEmpty()) throw new RuntimeException("队列为空,无法获取头部数据");
        return arr[++front];
    }
}


4 环形队列

4.1 介绍

  • 因为3中的队列是一次性的,所以构造环形队列,使队列可以重复利用!
  • 环形队列具体内容参考《大话数据结构》p114-p117非常重要
  • 之所以rear指向一个空的空间,是为了区分rear == front到底是空还是满!rear == front的时候为空,而(rear指向的空空间的索引 +1 )%maxSize后如果 == front,那就是满了

在这里插入图片描述

4.2 代码实现


package MyQueue;

public class CircleQueue {

    private int maxSize;    // 队列最大容量
    private int front;      // 队列头指针,默认0
    private int rear;       // 指向队列最后一个元素的后一个位置,且该位置只能为空,默认0
    private int[] arr;      // 数组,用来模拟队列,保存数据

    // 构造器
    public CircleQueue(int maxSize1){
        this.maxSize = maxSize1+1;
        arr = new int[maxSize];
        front = 0;
        rear = 0;
    }

    // 判断队列是否满了,如果尾指针==最大值-1(尾指针0开始),表示满了
    public boolean isFull(){
        return (rear+1)%maxSize == front;
    }

    // 判断队列是否为空,如果头尾指针相同,就是空了
    public boolean isEmpty(){
        return front==rear;
    }

    // 添加数据到队列
    public void addQueue(int n){
        if (isFull()) {
            System.out.println("队列已满,无法添加数据!");
            return;
        }
        arr[rear] = n;
        rear = (rear+1)%maxSize;
    }

    // 获取队列数据,即从头部拿出数据
    public int getQueue(){
        if(isEmpty()){
            // 不能return,因为return的值可能是队列元素的值
            throw new RuntimeException("队列为空,不能取数据");
        }
        int res = arr[front];
        front = (front+1)%maxSize;
        return res;
    }

    // 打印队列所有的内容,就算有取出的数据,那也只是返回了,而arr中并没有删除!
    public void showAllQueue(){
        if (isEmpty()){
            System.out.println("队列为空,没有数据");
            return;
        }
        for (int i=front; i<front+getSize(); i++){
            System.out.printf("arr[%d] == %d\t", i%maxSize, arr[i%maxSize]);
        }
    }

    // 获取队列长度
    public int getSize(){
        return (rear - front + maxSize)%maxSize;
    }

    // 获取头部数据
    public int getHead(){
        if (isEmpty()) throw new RuntimeException("队列为空,无法获取头部数据");
        return arr[front];  // front直接指向第一个元素,返回之后他不需要++,取队列需要++;
    }
}




5 链表

5.1 参考大话数据结构

5.2 单链表

5.2.1 直接插入到尾节点单链表代码实例
package LinkedList;

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        Node n1 = new Node(1,1);
        Node n2 = new Node(2,2);
        Node n3 = new Node(3,3);
        SingleLinkedList s1 = new SingleLinkedList();
        s1.addNode(n1);
        s1.addNode(n2);
        s1.addNode(n3);
        s1.list();
        // out message
        // Node{num=1, data=1}
        // Node{num=2, data=2}
        // Node{num=3, data=3}
    }
}

class SingleLinkedList {
    // 头节点不能动
    private Node head = new Node(0, 0);

    // 添加一个新节点
    public void addNode(Node n){
        // temp拿到临时节点,开始遍历
        Node temp = head;

        // 找到最后一个节点,然后把新节点直接添加上去
        while (true){
            // 如果当前节点next=null,表示当前为尾节点,则跳出循环
            if (temp.next == null){
                break;
            }
            // 否则就看下一个节点
            temp = temp.next;
        }
        // 添加节点
        temp.next = n;
    }

    // 显示链表
    public void list(){
        // first to assert isEmpty
        if (head.next == null){
            System.out.println("the list is empty!");
            return;
        }
        // head node we can not use, so make temp = head node.
        // because if head node is used, it will point other value
        Node temp = head.next;
        // 判断最后一个节点是否为空,为空则表示当前节点为最后一个节点,直接推出循环。
        // 不为空就输出节点信息,并判断下一个。
        while (true){
            if (temp == null){
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }

    }
}

class Node{
    public int num;     // 默认0
    public int data;    // 默认0
    public Node next;   // 默认null

    public Node(int num, int data){
        this.num = num;
        this.data = data;
    }

	// 没有+next,不然就套娃了
    @Override
    public String toString() {
        return "Node{" +
                "num=" + num +
                ", data=" + data +
                '}';
    }
}

5.2.2 带排序的单链表代码实现
  • 根据num进行节点排序插入,从小到大
  • 先找到插入的位置,temp节点,必须是插入位置的前一个节点。
  • 只需要添加一个addbynum方法即可。
 
	 // 根据节点num从小到大排序
    public void addNodeSortByNum(Node n){
        // 不能修改head结点,所以使用temp == head
        // head 节点应该位于插入节点的前一个位置,否则无法插入
        // 因为没有前驱指针,找不到前一个节点
        Node temp = head;
        boolean flag = false;   // 添加的编号num如果存在了,就变成true
        // 找到我们需要插入的位置节点temp,此节点是新插入node的前一个节点
        while (true){
            if (temp.next == null){
                // 如果temp.next == null,则表示此temp是尾节点,且就是我们要找的节点
                break;
            }
            if (temp.next.num > n.num){
                // 如果temp后一个节点num > 插入节点num,则表示应该插入在
                // temp节点和temp.next节点中间
                // 此时的temp就是我们要找的节点
                break;
            }
            else if (temp.next.num==n.num){
                // 添加的编号已经存在了
                flag = true;
                break;
            }
            temp = temp.next;   // 没有找到,判断下一个节点
        }

        if (flag){
            System.out.println("插入的编号已经存在, 编号是: "+ n.num);
        }
        else{
            // 插入到链表中,temo的后面
            n.next = temp.next;
            temp.next = n;
        }
    }
5.2.3 修改节点,删除节点
 public void updateNodeByNum(Node n){
        if (head.next==null){
            System.out.println("链表为空!");
            return;
        }
        // 找到需要修改的节点
        Node temp = head.next;
        boolean flag = false;   // true表示找到了

        while (true){
            if (temp == null){
                break;  // 已经遍历结束了,没有找到,直接退出即可
            }
            if (temp.num == n.num){
                // 找到了
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag){
            // 找到了,修改节点的data数据
            temp.data = n.data;
        }
        else {
            // 没有找到
            System.out.printf("没有找到节点编号为%d的节点,不能修改\n", n.num);
        }
    }
  • 删除节点,由于是单链表,所以我们如果要删除红色箭头所指的节点,必须找到他的前一个节点(绿色所指),否则删除的节点前一个节点不能连上删除节点的后一个节点,形成不了新链表。
  • 找到之后,temp.next = temp.next.next即可。
  • 被删除的节点没有引用,就会被GC回收。
    在这里插入图片描述
public void deleteNodeByNum(int num){
        // 判断链表是否为空
        if (head.next == null){
            System.out.println("链表为空");
            return;
        }

        Node temp = head;
        boolean flag = false;
        while (true){
            if (temp.next.num == num){
                // 找到了
                flag = true;
                break;
            }
            if (temp.next == null){
                // 遍历结束, 没找到
                break;
            }
            temp = temp.next;   // 没找到,后移
        }
        if (flag){
            temp.next = temp.next.next;
            System.out.println("删除完毕");
        }
        else {
            System.out.println("没有找到该节点,删除失败");
        }
    }
5.2.5 双向链表
5.2.6 循环链表
  • 有单向循环链表,有双向循环链表
  • 单向循环链表解决约瑟夫问题
  • 我们用尾指针替代头指针,因为头指针找到头节点是O(1),找到尾节点是O(n)。所以让rear尾节点指向头节点的prior节点(即最后一个节点),此时找头是rear.next,找第一个节点(头节点后面的节点)为rear.next.next,找尾节点就是自己O1。
  • 如何判断循环链表满了, temp.next != head 就是没有Full
  • 判断空还是 temp.next == null

6 栈

6.1 应用场景

在这里插入图片描述

6.2 代码表示

在这里插入图片描述

package MyStack;

public class demo {
    public static void main(String[] args) {
        MyStack m1 = new MyStack(10);
        m1.push(1);
        m1.push(2);
        m1.push(3);
        m1.push(4);
        m1.push(5);
        m1.push(6);
        m1.push(7);
        m1.push(8);
        m1.push(9);
        m1.push(10);
        m1.list();
        System.out.println(m1.pop());
        System.out.println(m1.pop());
        System.out.println(m1.pop());
        System.out.println(m1.pop());
        System.out.println(m1.pop());
        System.out.println(m1.pop());
        System.out.println(m1.pop());
        System.out.println(m1.pop());
        System.out.println(m1.pop());
        System.out.println(m1.pop());
    }
}


class MyStack{
    private int maxSize;    // stack的大小,索引因该是-1,所以top<=maxSize-1
    private int top = -1;   // top栈顶
    private int[] stack;    // 数组模拟栈

    public MyStack(int maxSize){
        this.maxSize = maxSize;
        stack = new int[this.maxSize];
    }

    // 判断栈是否满了
    public boolean isFull(){
        return top == maxSize-1;
    }

    // 判断栈空
    public boolean isEmpty(){
        return top == -1;
    }

    // 入栈
    public void push(int value){
        if (isFull()){
            System.out.println("stack is full");
            return;
        }
        stack[++top] = value;
    }

    // 出栈
    public int pop(){
        if (isEmpty()){
            throw new RuntimeException("stack is empty");
        }
        return stack[top--];
    }

    // 遍历栈,从栈顶开始显示数据
    public void list(){
        if (isEmpty()){
            System.out.println("stack is empty");
            return;
        }
        // 下面方式不行,因为修改了top值,所以要用temp取代top
        // while ( !(top<0) ){
        //     System.out.println(stack[top--]);
        // }
        int temp = top;
        while ( !(temp<0) ){
            System.out.println("stack[" + temp + "] = " + stack[temp]);
            temp--;
        }

        // 视频方法
        // for (int i=top; i>=0;i--){
        //     System.out.printf("stack[%d] = %d\n", i, stack[i]);
        // }
    }
}

6.3 四则运算案例

在这里插入图片描述
https://www.bilibili.com/video/BV1E4411H73v?p=33&spm_id_from=pageDriver

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值