数据结构与算法(基础)(Java)

数据结构与算法(基础版)

视频地址:https://www.bilibili.com/video/BV13g41157hK?t=2.3&p=3

一.时间复杂度及排序算法

1.master公式(算递归时间复杂度):

T(N) = a*T(N/b) + O(N^d)

1) log(b,a) > d -> 复杂度为O(N^log(b,a) )

2) log(b,a) = d -> 复杂度为O(N^d *logN)

3) log(b,a) < d -> 复杂度为O(N^d)

补充阅读:www.gocalf.com/blog/algorithm-complexity-and-master-theorem.html

2.归并排序(时间复杂度为O(N*logN))

(找小和问题):分成左右两边,递归成两边分别有序,双指针从左往右,从小到大复制到help数组

3.快速排序(可优化至O(N*logN))

(荷兰国旗问题):分批次递归,把目标值按在中间

① 快排 1.0 和 2.0 时间复杂度均为O(N^2),这里不赘述

② 快排3.0时间复杂度数学期望为O(N*logN)

package chapter.class01;

import java.util.Arrays;

public class main {
    public static void main(String[] args) {
        int[] arr={1,6,7,8,3,5,6,7,3};
        quickSort(arr);
        for(int i=0;i<arr.length;i++) {
            System.out.println(arr[i]);
        }
    }

    public static void quickSort(int[] arr){
        if(arr == null || arr.length<2){
            return;
        }
        quickSort(arr , 0 , arr.length-1);
    }

    public static void quickSort(int[] arr,int L,int R){
        if(L<R){
            swap(arr,L + (int)(Math.random() * (R-L+1)), R);
            int[] p = partition(arr,L,R);
            quickSort(arr,L,p[0]-1);//   <区
            quickSort(arr,p[1]+1,R);//   >区
        }
    }
    //此函数返回一个长度为二的数组res,res[0]为左边界,res[1]为右边界
    public static int[] partition(int[] arr,int L,int R){
        int less = L-1;//    <区右边界
        int more = R;//      >区左边界
        while(L<more){//     L表示当前数的位置   ,arr[R]   --》划分值
            if(arr[L]<arr[R]){//    当前数   <   划分值
                swap(arr,++less,L++);
            }else if(arr[L]>arr[R]){//  当前数  >  划分值
                swap(arr,--more,L);
            }else{
                L++;
            }
        }
        swap(arr,more,R);
        return new int[] {less+1,more};
    }
    public static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

4.堆排序

①通过heapinsert和heapify 来进行反复找最大值放在堆尾
package chapter.class01;

public class class01_heapSort {
    public static void main(String[] args) {
        int[] arr={7,4,4,6,3,0,7,1,3,9};
        heapSort(arr);
    }

    public static void heapSort(int[] arr){
        if(arr == null|| arr.length < 2){
            return;
        }
        for(int i=0;i<arr.length;i++){
            heapInsert(arr,i);
        }
        int heapSize = arr.length;
        swap(arr,0,--heapSize);
        while(heapSize > 0){
            heapify(arr,0,heapSize);
            swap(arr,0,--heapSize);
        }
    }

    //某个数在index位置,往上继续移动
    public static void heapInsert(int[] arr,int index){
        while(arr[index] > arr[(index - 1) / 2]){
            swap(arr,index,(index-1)/2);
            index = (index-1)/2;
        }
    }

//某个数在index位置,能否往下移动
    public static void heapify(int[] arr,int index,int heapSize){
        int left = index*2+1;//左孩子的下标
        while(left < heapSize){//下方还有孩子的时候
            //两个孩子中,谁的值大,把下标给largest
            int largest = left+1<heapSize && arr[left+1] > arr[left] ? left +1 :left;
            //父和较大的的孩子之间,谁的值大,把下表给largest
            largest = arr[largest] > arr[index] ? largest : index;
            if(largest == index){
                break;
            }
            swap(arr,largest,index);
            index = largest;
            left = index * 2 + 1;
        }
    }


    public static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

5.比较器(Java特有)

(没咋听懂,后面再看)

6.总结

快排最快,堆排所需空间少,归并稳定

二.链表

1.哈希表

2.有序表

与哈希表区别:时间复杂度更高,元素有序(个人理解)

3.链表(此部分内容大部分为coding问题)

三.二叉树

1.用递归和非递归两种方式实现先序,中序,后序遍历

(1)递归:

递归序:每个节点会三次回到自己,在此基础上引申出了先序,中序,和后序遍历

①先序遍历(头左右):在递归序的基础上,第一次出现时打印(或进行操作)

public static void preOrderRecur(Node head){
        if(head == null){
            return;
        }
        System.out.print(head.value + " ");
        preOrderRecur(head.left);
        preOrderRecur(head.right);
    }

②中序遍历(左头右):在递归序的基础上,第二次出现时打印(或进行操作)

public static void inOrderRecur(Node head){
        if(head == null){
            return;
        }
        inOrderRecur(head.left);
        System.out.print(head.value + " ");
        inOrderRecur(head.right);
    }

③后序遍历(左右头):在递归序的基础上,第三次出现时打印(或进行操作)

public static void posOrderRecur(Node head){
        if(head == null){
            return;
        }
        posOrderRecur(head.left);
        posOrderRecur(head.right);
        System.out.print(head.value + " ");
    }
(2)非递归:

①先序遍历(同上):

第一步:从栈中弹出一个节点car;

第二步:打印(或处理)car;

第三步:先压右再压左(如果有的话),没有就不操作;

第四步:重复上述步骤;

public static void preOrderUnRecur(Node head){
        if(head != null){
            Stack<Node> stack = new Stack<Node>();
            stack.add(head);
            while(!stack.isEmpty()){
                head = stack.pop();
                System.out.print(head.value + " ");
                if(head.right != null){
                    stack.push(head.right);
                }
                if(head.left != null){
                    stack.push(head.left);
                }
            }
        }
        System.out.println();
    }

②中序遍历(同上):

第一步:对于每一颗子树,左边界全部进栈(从父到子);

第二步:依次弹出的过程中,打印;

第三步:弹出节点的右树重复上述步骤(如果有的话);

public static void inOrderUnRecur(Node head){
        if(head != null){
            Stack<Node> stack = new Stack<Node>();
            while(!stack.isEmpty() || head != null){
                if(head != null){
                    stack.push(head);
                    head = head.left;
                }else{
                    head = stack.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
        }
        System.out.println();
    }

③后序遍历(同上):(略有不同的地方是,用了两个栈)

此方法本质(个人理解):利用栈的特点,从头右左的遍历顺序逆序出 栈,变成左右头。

第一步:从栈中弹出一个节点car;

第二步:car放入收集栈;

第三步:先压左再压右;

第四步:重复上述步骤;

public static void posOrderUnRecur(Node head){
        if(head != null){
            Stack<Node> s1 = new Stack<Node>();
            Stack<Node> s2 = new Stack<Node>();
            s1.push(head);
            while(!s1.isEmpty()){
                head = s1.pop();
                s2.push(head);
                if(head.left != null){
                    s1.push(head.left);
                }
                if(head.right != null){
                    s1.push(head.right);
                }
            }
            while(!s2.isEmpty()){
                System.out.print(s2.pop().value + " ");
            }
        }
        System.out.println();
    }

2.完成二叉树的宽度优先遍历(宽度遍历用队列)

二叉树的深度优先遍历即是先序遍历;

①从队列弹出一个节点;

②先放左,再放右;

public static void widthFirstOrder(Node head){
        if(head == null){
            return;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        while(!queue.isEmpty()){
            Node cur = queue.poll();
            System.out.println(cur.value);
            if(cur.left != null){
                queue.add(cur.left);
            }
            if(cur.right != null){
                queue.add(cur.right);
            }
        }
    }

3.二叉树的相关概念

  1. ①搜索二叉树(BST):对于每一棵子树,它的左树都比它小,右树都比它大;

②如何判断搜索二叉树:中序遍历,如果某个位置存在降序,则不是搜索二叉树,即搜索二叉树中序遍历结果一定是升序;

第一种代码:

public static int preValue = Integer.MIN_VALUE;//设置一个全局变量用来比较是否为升序;此语句意为整数(Integer)的最小值
    public static boolean checkBST(Node head){
        if(head == null){
            return true;//如果树为空,认为是搜索二叉树
        }
        boolean isLeftBST = checkBST(head.left);//判断左边是否为搜索二叉树,将结果返回给变量用于后续判断
        if(!isLeftBST){
            return false;//如果左边已经不为搜索二叉树,则直接返回false
        }
        if(head.value <= preValue){
            return false;//如果左边树的最后一个节点比preValue小的话,则不为升序,直接返回false
        }else{
            preValue = head.value;//如果大于的话,那就赋值给preValue,相当于将其作为指针遍历下去(个人理解)
        }
        return checkBST(head.right);//右树结果直接返回
    }

第二种代码(运用递归思想):

public static boolean isBST(Node head){
        return process(head).isBST;
    }

    public static class ReturnData{
        public boolean isBST;
        public int min;
        public int max;
        public ReturnData(boolean is,int mi,int ma){
            isBST = is;
            min = mi;
            max = ma;
        }
    }

    public static ReturnData process(Node x){
        if(x == null){
            return null;
        }
        ReturnData leftData = process(x.left);
        ReturnData rightData = process(x.right);
        //找最值
        int min = x.value;
        int max = x.value;
        if(leftData != null){
            min = Math.min(min, leftData.min);
            max = Math.max(max, leftData.max);
        }
        if(rightData != null){
            min = Math.min(min, rightData.min);
            max = Math.max(max, rightData.max);
        }
        //判断isBST
        //第一种方法:
        boolean isBST = true;
        if(leftData!=null && (!leftData.isBST || leftData.max >= x.value)){
            //如果左树有东西,且左树不为搜索二叉树,则直接false,或者如果左树有东西,且左树的最大值比根节点的值大,也直接				  false
            isBST = false;
        }
        if(rightData != null && (!rightData.isBST || x.value >= rightData.min)){
            //同上面的思路,如果右树有东西,且右树不为搜索二叉树,则直接false,或者如果右树有东西,且右树的最小值比根节点的值			  小,则直接false
            isBST = false;
        }
        //第二种方法:
        
        /*boolean isBST = false;
        if(
                (leftData!=null ? (leftData.isBST && leftData.max < x.value):true)
                &&
                (rightData != null ? (rightData.isBST && rightData.min > x.value) : true)        
        ){
            isBST = true;
        }*/
        
        return new ReturnData(isBST,min,max);//返回三个数据,是否为搜索二叉树,树的最小值与最大值
    }
  1. ①完全二叉树(CBT):每一层都是满的或者最后一层不满但从左到右是满的(比较拗口,建议去百度一手)

②如何判断完全二叉树:宽度优先遍历,(1)对于任意节点,如果有右无左直接false;(2)在(1)条件满足的情况下,如果第一次遇到了一个节点左右子不全(其实就是只有左节点或者本身为叶节点),那么后续节点必须全为叶节点(无子节点),如果不满足,则不为完全二叉树

public static boolean isCBT(Node head){
        if(head == null){
            return true;
        }
        LinkedList<Node> queue = new LinkedList<>();
        boolean leaf = false;
        Node l = null;
        Node r = null;
        queue.add(head);
        while(!queue.isEmpty()){
            head = queue.poll();
            l = head.left;
            r = head.right;
            if((leaf && (l != null  || r != null))||(l ==null && r != null)){
                return false;
            }

            if(l != null){
                queue.add(l);
            }
            if(r != null){
                queue.add(r);
            }
            if(l == null || r == null){
                leaf = true;
            }
        }
        return true;
    }

3.①满二叉树:一棵高度为h,并且含有2^h - 1个结点的二叉树称为满二叉树,即树中的每一层都含有最多的结点。满二叉树的叶子节点都集中在二叉树的最下一层,并且除叶子结点之外的每个结点度数均为2.(二叉树结点的度即为结点的孩子个数);

②如何判断满二叉树:可以直接根据定义,先求树的最大深度h,再求节点个数s,需要满足s=2^h-1;

public static boolean isFBT(Node head){
        if(head == null){
            return true;
        }
        ReturnData data = process(head);

        return data.nodes == (1<<data.height - 1);
    }

    public static class ReturnData{
        public static int height;
        public static int nodes;
        public ReturnData(int h,int n){
            height = h;
            nodes = n;
        }
    }

    public static ReturnData process(Node x){
        if(x == null){
            return new ReturnData(0,0);
        }

        ReturnData leftData = process(x.left);
        ReturnData rightData = process(x.right);

        int height = Math.max(leftData.height,rightData.height)+1;
        int nodes  = leftData.nodes+rightData.nodes+1;

        return new ReturnData(height,nodes);
    }

4.①平衡二叉树(AVL):对于任何一个子树,它的左树和右树高度差不超过1;

②如何判断平衡二叉树:首先需要满足根节点左树和右树都为平衡二叉树,再要满足左树的高度与右树的高度差不大于1;

 public static boolean isBalanced(Node head){
        return process(head).isBalanced;
    }

    public static class ReturnType{
        public boolean isBalanced;
        public int height;

        public ReturnType(boolean isB,int hei){
            isBalanced = isB;
            height = hei;
        }
    }

    public static ReturnType process(Node x){
        if(x == null){
            return new ReturnType(true,0);
        }
        ReturnType leftData = process(x.left);
        ReturnType rightData = process(x.right);
        int height = Math.max(leftData.height, rightData.height) + 1;
        boolean isBalanced = leftData.isBalanced && rightData.isBalanced && Math.abs(leftData.height - rightData.height)<2;
        return new ReturnType(isBalanced,height);
    }

图的内容在后面,敬请期待...

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构算法Java编程中扮演着非常重要的角色。数据结构是指在计算机中存储和组织数据的方式,而算法则是指解决问题的步骤和策略。了解和掌握数据结构算法对于编写高效且可维护的Java程序至关重要。 在Java中,有许多常见的数据结构算法可以使用。比如数组、栈、队列、链表、树等。这些数据结构可以帮助我们在处理不同类型的数据时更加高效地存储和访问数据。同时,各种排序算法如冒泡排序、选择排序、插入排序、归并排序、快速排序等也是在Java编程中经常使用的算法。 通过学习和应用数据结构算法,我们可以提高程序的执行效率和性能。在面试中,数据结构算法的知识也是经常被考察的内容。因此,掌握Java中的数据结构算法对于提升编程能力和面试竞争力都非常重要。 引用提供了一些常用数据结构的介绍,包括数组、栈、队列、链表、树和图等。而引用则提供了一些常见的排序算法的介绍,如冒泡排序、选择排序、插入排序、归并排序、快速排序等。通过学习和实践这些数据结构算法,可以帮助我们更好地理解和应用于Java编程中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [数据结构算法详解(含算法分析、动图图解、Java代码实现、注释解析)](https://blog.csdn.net/yuan2019035055/article/details/120262225)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值