尚硅谷Java数据结构与java算法 全194章笔记整理

前言

视频地址:https://www.bilibili.com/video/BV1E4411H73v?from=search&seid=13120683720695451628

评价:整个教程的数据结构部分讲的挺好的,知识点全都覆盖了,而且每个数据结构都有代码解释,但是最后20节算法部分讲的有点乱,算法部分我决定直接刷leetcode了

数组

稀疏数组:

二维数组的省内存的保存方法,一般是n行3列,三列分别为行,列,值。

二维数组转稀疏数组:

  1. 遍历整个二维数组,查看有多少个有效数字
  2. 根据有效数字的个数,创建稀疏数组
  3. 遍历二维数组,将有效的数字放入稀疏数组中

稀疏数组转二维数组:

  1. 根据稀疏数组第一行建立空二维数组

  2. 读取稀疏数组后几行数据,插入二维数组中

代码实现:

package com.dataStructure;
public class sparseArray {
   
    public static void main(String[] args){
   
        //新建数组
        int Arr [][] = new int[11][11];
        Arr[1][2] = 1;
        Arr[2][3] = 2;
        Arr[3][4] = 3;

        //输出初始数组
        for(int[] row : Arr){
   
            for(int data : row){
   
                System.out.printf("%d\t",data);
            }
            System.out.print("\n");
        }

        //将二维数组转换为稀疏数组
        //1 遍历,得到非0个数
        int sum = 0;
        for (int i = 0; i < 11; i++){
   
            for (int j = 0; j < 11; j++){
   
                if (Arr[i][j] != 0){
   
                    sum++;
                }
            }
        }
        //2 创建稀疏数组
        int[][] sparsArr = new int[sum+1][3];
        //第一行
        sparsArr[0][0] = 11;
        sparsArr[0][1] = 11;
        sparsArr[0][2] = sum;
        //输入值
        int count = 1;      //用于记录sparseArr的行
        for (int i = 0; i < 11; i++){
   
            for (int j = 0; j < 11; j++){
   
                if (Arr[i][j] != 0){
   
                    sparsArr[count][0] = i;
                    sparsArr[count][1] = j;
                    sparsArr[count][2] = Arr[i][j];
                    count++;
                }
            }
        }
        //3 输出稀疏数组
        System.out.println();
        System.out.println("得到的稀疏数组为~~~");
        for(int i = 0 ;i< sparsArr.length ;i++){
   
            System.out.printf("%d\t%d\t%d\t\n",sparsArr[i][0],sparsArr[i][1],sparsArr[i][2]);
        }

        //将二维数组转换回稀疏数组
        //1 新建数组
        int[][] Arr_back = new int[sparsArr[0][0]][sparsArr[0][1]];

        //2 将有值的位置安回原来的位置
        for (int i = 1; i<sparsArr.length;i++){
   
            Arr_back[sparsArr[i][0]][sparsArr[i][1]] = sparsArr[i][2];
        }

        //输出数组
        System.out.println();
        System.out.print("Arr_back输出为~~~\n");
        for(int i = 0;i<sparsArr[0][0];i++){
   
            for(int j = 0;j<sparsArr[0][1];j++){
   
                System.out.print(Arr_back[i][j]+"   ");
            }
            System.out.println();
        }
    }
}

队列

队列是一个有序列表,可能用数组或链表来实现。

先进入的数据先取出,后进入的数组后取出。

数组模拟队列的思路:

队列本身就是一个有序列表,maxSize来记录该数组最大容量;front和rear分别记录队列前后端的下标,front随着数据输出而改变,rear随着数据输入而改变。

加入数据:若队列不为满,尾部指针后移:rear+1

拿出数据:若队列不为空,前端指针后移:front+1

代码:

package com.dataStructure;
import java.util.Scanner;

public class arrayQueue {
   
    public static void main(String[] args) {
   
        Queue arrayQueue = new Queue(3);
        String key = "";
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;

        while (loop) {
   
            //输出一个菜单
            System.out.println("press s to show Queue");
            System.out.println("press e to exit program");
            System.out.println("press a to add element");
            System.out.println("press g to get data");
            key = scanner.next();
            switch (key) {
   
                case "s":
                    arrayQueue.showQueue();
                    break;
                case "e":
                    scanner.close();
                    loop = false;
                    break;
                case"a":
                    System.out.println("Input a word:");
                    int value = scanner.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case"g":
                    System.out.println(arrayQueue.getQueue());
                    break;
            }
        }
    }
}

class Queue {
   
    //装入👴需要的私有变量
    private int rear = -1;
    private int front = -1;
    private int maxSize;
    private int[] que;

    //设置Queue的规格
    public Queue(int size) {
   
        maxSize = size;
        que = new int[size];
    }

    //判断Queue是否为空
    public boolean isEmpty() {
   
        return rear == front;
    }

    //判断Queue满了没
    public boolean isFull() {
   
        return rear == maxSize - 1;
    }

    //加入一个新元素
    public void addQueue(int n) {
   
        if (isFull()) {
   
            System.out.println("Queue is full! can not add!");
            return;
        }
        rear++;
        que[rear] = n;
    }

    //删除一个元素
    public int getQueue() {
   
        if (isEmpty()) {
   
            throw new RuntimeException("Queue is empty! can not delete!");
        }
        front++;
        return que[front];
    }

    //showQueue
    public void showQueue() {
   
        if (isEmpty()) {
   
            System.out.println("队列空,无数据,无法showQueue");
        } else {
   
            for (int i = 0; i < que.length; i++) {
   
                System.out.printf("Queue[%d] = %d\n", i, que[i]);
            }
        }
    }

}


这时候存在一个问题,数组使用一次之后就不能用了,没有达到复用的效果,例如,我将位置填满后再全部删去,这时候我就没办法再add新的元素进去了。

使用数组模拟形成一个环形队列,利用取模的方式实现。

使用数组模拟环形队列

思路:

  1. Front作为队列第一个元素的引用,初始front = 0

  2. Rear作为队列最后一个元素的后一个元素的引用,初始rear = 0

  3. 队列满时,条件为(rear+1)%maxSize = front。

这里要注意的是,虽然maxSize是5,但是我们实际可以使用的只有4个。当总共有5个格子时,分别为0,1,2,3,4,满了的时候,rear = 4,front = 0,maxSize = 5;带入公示4+1模除5 = front = 0。

  1. 队列空时,rear = front。

当rear-1=front时,实际上只有一个元素,就是front所在的位置;当rear = front的时候,最后一个元素rear-1在最初的元素front之前,所以这种情况不存在。

  1. 队列中有效数字的个数为(rear-front+maxSize)% maxSize。

综合4来理解,rear-1 = front时候,存在一个元素,rear = front的时候,不存在元素,maxSize%maxSize永远为0,所以很显然(rear-front)% maxSize就是存在有效数字的个数。这里一定要加maxSize再取模,因为rear-front可能是负数,所以必加。

代码:

package com.dataStructure;

import java.util.Scanner;

public class arrayCircleQueue {
   
    public static void main(String[] args) {
   
        CircleQueue arrayQueue = new CircleQueue(4);
        String key = "";
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;

        while (loop) {
   
            //输出一个菜单
            System.out.println("press s to show Queue");
            System.out.println("press e to exit program");
            System.out.println("press a to add element");
            System.out.println("press g to get data");
            key = scanner.next();
            switch (key) {
   
                case "s":
                    arrayQueue.showQueue();
                    break;
                case "e":
                    scanner.close();
                    loop = false;
                    break;
                case"a":
                    System.out.println("Input a word:");
                    int value = scanner.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case"g":
                    System.out.println(arrayQueue.getQueue());
                    break;
            }
        }
    }
}

class CircleQueue {
   
    //装入👴需要的私有变量
    private int rear = 0;
    private int front = 0;
    private int maxSize;
    private int[] que;

    //设置Queue的规格
    public CircleQueue(int size) {
   
        maxSize = size;
        que = new int[size];
    }

    //判断Queue是否为空
    public boolean isEmpty() {
   
        return rear == front;
    }

    //判断Queue满了没
    public boolean isFull() {
   
        return (rear+1)%maxSize == front;
    }

    //加入一个新元素
    public void addQueue(int n) {
   
        if (isFull()) {
   
            System.out.println("Queue is full! can not add!");
            return;
        }
        que[rear] = n;
        rear = (rear+1)%maxSize;
    }

    //删除一个元素
    public int getQueue() {
   
        if (isEmpty()) {
   
            throw new RuntimeException("Queue is empty! can not delete!");
        }
        int value = que[front];
        front = (front+1)%maxSize;
        return value;
    }

    //showQueue
    public void showQueue() {
   
        if (isEmpty()) {
   
            System.out.println("队列空,无数据,无法showQueue");
        } else {
   
            for (int i = front; i < front+(rear+maxSize-front)%maxSize; i++) {
   
                System.out.printf("Queue[%d] = %d\n", i%maxSize, que[i%maxSize]);
            }
        }
    }
}


链表

单链表

链表每个节点包含data域,next域:指向下一个节点

创建HeroNode类,其中包含HeroNode next;

创建LinkedList类,先创建一个头节点

遍历思路:

通过一个辅助变量来遍历

一般来说,展示链表中所有数据要先判断链表是否为空。

添加内容思路:

  1. 直接添加到尾部:直接加到最后面(遍历找到最后面,再添加)

  2. 按顺序添加:找到要添加的位置,新节点.next = temp.next,temp.next = 新节点。

修改节点的思路:

  1. 先找到这个节点(遍历)

  2. 修改数值

删除节点的思路:

  1. 找到要删除节点的前一个节点

  2. Node.next = node.next.next;

  3. 被删除的节点将没有其他引用指向它,会被垃圾回收机制回收

循环要注意的点!!!

一般来说,写while循环的时候,内容的顺序是:1)输出,2)if边界的break测试,3)指针后移

循环代码:


void show(){
   
        NodeStack temp = head.next;
        while(true){
   
            System.out.println("栈中的值为"+temp.value);
            if(temp.next == null){
   
                break;
            }
            temp = temp.next;

        }
    }

单链表代码:

package com.dataStructure;

public class linkedList {
   
    public static void main(String[] args){
   
        HeroNode heroNode1 = new HeroNode(1,"阿毛","amao");
        HeroNode heroNode2 = new HeroNode(2,"阿狗","agou");
        HeroNode heroNode3 = new HeroNode(3,"阿缺","aque");

        singleLinkedList list_test = new singleLinkedList();
        //乱序输入结果
        list_test.addSpecial(heroNode1);
        list_test.addSpecial(heroNode3);
        list_test.addSpecial(heroNode2);
        list_test.show();

        //测试修改
        HeroNode heroNode4 = new HeroNode(3,"阿","a");
        list_test.update(heroNode4);
        System.out.println("修改后的结果~~~");
        list_test.show();

        //删除测试
        int test_num = 1;
        list_test.del(test_num);
        System.out.println("删除后的结果");
        list_test.show();





    }
}

class HeroNode{
   
    int no;
    String name;
    String nickName;
    HeroNode next;

    public HeroNode(int no,String name,String nickName){
   
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString(){
   
        return "Hero_no = "+no+" Hero_name = "+name+" Hero_nickname = "+nickName ;
    }
}

class singleLinkedList{
   
    private HeroNode head = new HeroNode(0,"","");

    public void add(HeroNode heroNode){
   
        HeroNode temp = head;
        while(true){
   
            if(temp.next == null){
   
                break;
            }
            temp = temp.next;
        }
        temp.next = heroNode;
    }

    public void addSpecial(HeroNode heroNode){
   
        HeroNode temp = head;
        boolean label = false;
        while(true){
   
            if(temp.next == null){
   
                break;
            }
            if(heroNode.no<temp.next.no){
   
                break;
            }
            if(heroNode.no == temp.next.no){
   
                label = true;
                break;
            }
            temp = temp.next;
        }
        if(label){
   
            System.out.println("没位置了!爬!");
        }else {
   
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    public void update(HeroNode newNode) {
   
        HeroNode temp = head;
        boolean flag = false;
        while (true) {
   
            if (temp == null) {
   
                break;
            }
            if (temp.no == newNode.no) {
   
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag) {
   
            temp.name = newNode.name;
            temp.nickName = newNode.nickName;
        }else{
   
            System.out.println("can not find this no!");
        }

    }

    public void del(int num){
   
        HeroNode temp = head;
        boolean flag = false;
        while(true){
   
            if(temp.next == null){
   
                break;
            }
            if(temp.next.no == num){
   
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
   
            temp.next = temp.next.next;
        }else{
   
            System.out.println("没找到要删除的啊?");
        }
    }


    public void show(){
   
        if(head.next == null){
   
            System.out.println("列表为空,输出个犊子");
        }else{
   
            HeroNode temp = head.next;
            while(true){
   
                if(temp == null){
   
                    break;
                }
                System.out.println(temp);
                temp = temp.next;
            }
        }

    }
}

单链表的反转

思路:

  1. 先定义一个revertHead = new Node();

  2. 从头到尾遍历原来的表,依次取出节点,并将它放在revertHead的最前端(head后面)

  3. 原来的链表的head.next = revertHead.next,将除了head以外的内容还给原来的head

难点:

实现从原来的链表中遍历,并将元素挨个放到新链表的头部,需要多次调整两个链表的指向问题。

代码:

public void reverse(Node head) {
   
        if(head.next == null || head.next.next == null){
   return;}
        Node cur = head.next;
        Node next = null;
        Node revertHead = new Node(0);      

        while(cur!=null){
   
            next = cur.next;    //将cur后面的值暂时存储在next中
            cur.next = revertHead.next;     //让cur指向原来revertHead数组的第一个元素
            revertHead.next = cur;      //让revertHead指向cur,这样就实现了一种类似中间插入的操作(revertHead列表头处插入)
            cur = next;     //更新cur
        }
        head.next = revertHead.next;        //将一大串列表还给原来的head
}


倒叙打印单链表

思路1: 先反转链表,再打印链表

思路2: 利用栈,将单链表压入栈,再打印输出

思路2代码:

public void revertPrint(){
   
        Stack<Integer> stack = new Stack();
        if (head.next == null) {
   
            System.out.println("列表为空,输出个犊子");
        } else {
   
            Node temp = head.next;
            while (true) {
   
                if (temp == null) {
   
                    break;
                }
                stack.add(temp.value);
                temp = temp.next;
            }
        }
        while(stack.size()>0) {
   
            System.out.println(stack.pop());
        }
}


单链表所有功能代码

包含测试各种刚才提到的功能

package com.dataStructure;
import java.util.Scanner;
import java.util.Stack;

public class linkedList_test {
   
    public static void main(String[] args){
   
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);

        linkedList_test1 test1 = new linkedList_test1();
        test1.add(node1);
        test1.add(node2);
        test1.add(node3);

        //展示目前的情况
        System.out.println("目前链表的情况~~~");
        test1.show();

        //统计元素个数
        System.out.println("链表总数~~~");
        int res = test1.count();
        System.out.println(res);

        //输入一个数k,输出列表中倒数第k个数
//        System.out.println("please input a value");
//        Scanner scanner = new Scanner(System.in);
//        int k = scanner.nextInt();
//        System.out.println("倒数第 "+k+" 个数是 "+test1.find(test1,k));

        //翻转单链表
//        System.out.println("链表翻转结果~~~");
//        test1.reverse(test1.head);
//        test1.show();

        //逆序输出
        System.out.println("链表翻转输出结果为~~~");
        test1.revertPrint();
    }
}

class Node{
   
    int value;
    Node next;
    public Node(int value){
   
        this.value = value;
    }
    @Override
    public String toString(){
   
        return "my value is "+value;
    }
}

class linkedList_test1 {
   
    Node head = new Node(0);

    //加入一个元素
    public void add(Node nodeIn) {
   
        Node temp = head;
        while (true) {
   
            if (temp.next == null) {
   
                break;
            }
            temp = temp.next;
        }
        temp.next = nodeIn;
    }

    //打印所有元素
    public void show() {
   
        if (head.next == null) {
   
            System.out.println("列表为空,输出个犊子");
        } else {
   
            Node temp = head.next;
            while (true) {
   
                if (temp == null) {
   
                    break;
                }
                System.out.println(temp);
                temp = temp.next;
            }
        }
    }

    //计算一共多少个数
    public int count() {
   
        int counting = 0;
        Node temp = head.next;
        if (temp == null) {
   
            System.out.println("啥也没有,共0个元素");
        } else {
   
            while (true) {
   
                counting++;
                if (temp.next == null) {
   
                    break;
                }
                temp = temp.next;
            }
        }
        return counting;
    }

    //在test1中寻找倒数第k个数是多少
    public int find(linkedList_test1 test1, int k) {
   
        if((k>test1.count()) || (k<=0)){
   
            throw new RuntimeException("input error!");
        }else{
   
        Node temp = head;
        int sum = test1.count();
        int need = sum - k+1;       //我们需要正着数到第need个数
        int value = 0;      //作为一个判定条件

        while (true) {
   
            temp = temp.next;
            value++;
            if (value == need) {
   
                break;
            }
        }
        return temp.value;
    }
    }

//反转链表
    public void reverse(Node head) {
   
        if(head.next == null || head.next.next == null){
   return;}
        Node cur = head.next;
        Node next = null;
        Node revertHead = new Node(0);

        while(cur!=null){
   
            next = cur.next;    //将cur后面的值暂时存储在next中
            cur.next = revertHead.next;     //让cur指向原来revertHead数组的第一个元素
            revertHead.next = cur;      //让revertHead指向cur,这样就实现了一种类似中间插入的操作(revertHead列表头处插入)
            cur = next;     //更新cur
        }
        head.next = revertHead.next;        //将一大串列表还给原来的head
    }

//利用栈倒序输出链表
    public void revertPrint(){
   
        Stack<Integer> stack = new Stack();
        if (head.next == null) {
   
            System.out.println("列表为空,输出个犊子");
        } else {
   
            Node temp = head.next;
            while (true) {
   
                if (temp == null) {
   
                    break;
                }
                stack.add(temp.value);
                temp = temp.next;
            }
        }
        while(stack.size()>0) {
   
            System.out.println(stack.pop());
        }
    }


双向链表

双向链表相对于单链表来说:

  1. 查找的方向不止是一个方向

  2. 可以实现自我删除,不需要像单链表一样寻找要删除节点的前一个节点

思路分析:

遍历:和单向链表一致,可以前向,也可以后向。

添加元素到链表最后:1) 先遍历到这个链表的最后

​ 2) temp.next = newNode; newNode.pre = temp;

按照编号添加元素:1) 找到要添加的位置的前一个元素temp

​ 2) heroNode.next = temp.next;

​ 3) temp.next = heroNode;

​ 4) heroNode.next.pre = heroNode;

​ 5) heroNode.pre = temp; //这些步骤本质上就是将原来的两根指针变成四根

修改:找到这个节点直接修改就可以

自我删除:
1) 找到这个节点

​ 2) temp.pre.next = temp.next;

temp.next.pre. = temp.pre;

(当删除的节点是最后一个时,后面句话不需要,否则会出现空指针)

代码:

//构建一个双向列表的节点
class Node{
   
    int value;
    Node next;
    Node pre;
    public Node(int value){
   
        this.value = value;
    }
    @Override
    public String toString(){
   
        return "my value is "+value;
    }
}

//加入一个元素
    public void add(Node nodeIn) {
   
        Node temp = head;
        while (true) {
   
            if (temp.next == null) {
   
                break;
            }
            temp = temp.next;

        }
        temp.next = nodeIn;
        nodeIn.pre = temp;
    }

    //按序号加入元素
    public void addSpecial(Node heroNode){
   
        Node temp = head;
        boolean label = false;
        while(true){
   
            if(temp.next == null){
   
                break;
            }
            if(heroNode.value<temp.next.value){
   
                break;
            }
            if(heroNode.value == temp.next.value){
   
                label = true;
                break;
            }
            temp = temp.next
  • 12
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值