码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 数据结构与算法(二)


    文章目录

      • 数据结构与算法(二)
        • 1 时间复杂度、空间复杂度、排序算法和二分法
          • 1.1 简单的排序算法
          • 1.2 二分查找
        • 2 异或运算、进一步认识对数器的重要性
          • 2.1 不用额外变量交换两个数的值
          • 2.2 不用额外变量交换数组中两个数的值
          • 2.3 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
          • 2.4 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
          • 2.5 一个数组中有一种数出现K次,其他数都出现了M次
        • 3 单双链表、栈和队列、递归和Master公式、哈希表和有序表的使用和性能
          • 3.1 反转单链表、反转双链表
          • 3.2 在链表中删除指定值的所有节点
          • 3.3 用双链表实现栈和队列
          • 3.4 用环形数组实现栈和队列
          • 3.5 实现有getMin功能的栈
          • 3.6 两个栈实现队列
          • 3.7 两个队列实现栈
          • 3.8 用递归行为得到数组中的最大值,并用master公式来估计时间复杂度
          • 3.9 哈希表和有序表使用的code展示
        • 4 归并排序及其常见面试题
          • 4.2 数组小和
          • 4.3 数组的降序对
          • 4.4 大于2倍问题
        • 5 归并排序面试题(续)、快速排序
          • 5.1 区间和的个数
          • 5.2 荷兰国旗问题
          • 5.3 快速排序从1.0到3.0的实现
          • 5.4 快速排序的非递归实现
          • 5.5 双向链表进行快速排序
        • 6 比较器、堆结构、堆排序
          • 6.1 比较器使用的code展示
          • 6.2 堆结构的实现
          • 6.3 堆排序的实现
          • 6.4 基本有序的数组
        • 7 和堆有关的面试题、加强堆结构
          • 7.1 线段最大重合问题
          • 7.2 加强堆的实现
          • 7.3 得奖系统
        • 8 前缀树、不基于比较的排序(计数排序、基数排序)、排序算法的稳定性
          • 8.1 前缀树实现
          • 8.2 计数排序
          • 8.3 基数排序
          • 8.4 排序算法的稳定性
          • 8.5 排序算法总结

    数据结构与算法(二)

    1 时间复杂度、空间复杂度、排序算法和二分法

    • 内容:
      • 评估算法优劣的核心指标
      • 时间复杂度、空间复杂度、估算方式、意义
      • 常数时间的操作
      • 选择排序、冒泡排序、插入排序
      • 最优解
      • 对数器
      • 二分法
    1.1 简单的排序算法
    • 选择排序

    image.png

    • 冒泡排序

    image.png

    • 插入排序

    image.png

    1.2 二分查找
    • 有序数组中找到num

    image.png

    • 有序数组中找到>=num最左的位置

    image.png

    • 有序数组中找到<=num最右的位置

    image.png

    • 局部最小值问题,定义何为局部最小值:
      • arr[0] < arr[1],0位置是局部最小;
      • arr[N-1] < arr[N-2],N-1位置是局部最小;
        arr[i-1] > arr[i] < arr[i+1],i位置是局部最小;
      • 给定一个数组arr,已知任何两个相邻的数都不相等,找到随便一个局部最小位置返回

    image.png

    2 异或运算、进一步认识对数器的重要性

    • 内容:
      • 异或运算的性质
      • 异或运算的题目
    2.1 不用额外变量交换两个数的值
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    
    • 1
    • 2
    • 3
    2.2 不用额外变量交换数组中两个数的值
    public static void swap (int[] arr, int i, int j) {
       
       arr[i]  = arr[i] ^ arr[j];
       arr[j]  = arr[i] ^ arr[j];
       arr[i]  = arr[i] ^ arr[j];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.3 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
    • arr数组中,只有一种数出现奇数次
    public static void printOddTimesNum1(int[] arr) {
       
       int ans = 0;
       for (int x : arr) ans ^= x;
       System.out.println(ans);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.4 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数

    怎么把一个int类型的数 x,提取出二进制中最右侧的1?

    x & (~x + 1) 等价于 x & (-x)

    public static void printOddTimesNum2(int[] arr) {
       
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
       
            eor ^= arr[i];
        }
        // a 和 b是两种数
        // eor != 0
        // eor最右侧的1,提取出来
        // eor :     00110010110111000
        // rightOne :00000000000001000
        int rightOne = eor & (-eor); // 提取出最右的1
    
        int onlyOne = 0; // eor'
        for (int i = 0 ; i < arr.length;i++) {
       
            //  arr[1] =  111100011110000
            // rightOne=  000000000010000
            if ((arr[i] & rightOne) != 0) {
       
                onlyOne ^= arr[i];
            }
        }
        System.out.println(onlyOne + " " + (eor ^ onlyOne));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    2.5 一个数组中有一种数出现K次,其他数都出现了M次
    • 已知M > 1,K < M,找到出现了K次的数,要求额外空间复杂度O(1),时间复杂度O(N)
    // 对数器 for test
    public static int test(int[] arr, int k, int m) {
       
    	Map<Integer, Integer> counter = new HashMap<>();
    	for (int x : arr) {
       
    		counter.compute(x, (key, v) -> v == null ? 1 : v + 1);
    	}
    	for (Integer num : counter.keySet()) {
       
    		if (counter.get(num) == k) {
       
    			return num;
    		}
    	}
    	return -1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    public static int onlyKTimes(int[] arr, int k, int m) {
       
        int intBits = 32;
        int[] bitCounter = new int[intBits];
        // bitCounter[0]: 0 位置的1出现了几个
        // bitCounter[i]: i 位置的1出现了几个
        for (int x : arr) {
       
            for (int i = 0; i < intBits; i++) {
       
                //          if (((x >> i) & 1) != 0) {
       
                //             bitCounter[i] += 1;
                //          }
                bitCounter[i] += (x >> i) & 1;
            }
        }
        int ans = 0;
        for (int i = 0; i < intBits; i++) {
       
            if (bitCounter[i] % m != 0) {
       
                // 在第i位上有1
                ans |= (1 << i);
            }
        }
        return ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    题目变一下,如果能找到出现K次的数,就返回该数;如果找不到,返回-1。

    public static int onlyKTimes(int[] arr, int k, int m) {
       
        int intBits = 32;
        int[] bitCounter = new int[intBits];
        // bitCounter[0]: 0 位置的1出现了几个
        // bitCounter[i]: i 位置的1出现了几个
        for (int x : arr) {
       
            for (int i = 0; i < intBits; i++) {
       
                //				if (((x >> i) & 1) != 0) {
       
                //					bitCounter[i] += 1;
                //				}
                bitCounter[i] += (x >> i) & 1;
            }
        }
        int ans = 0;
        for (int i = 0; i < intBits; i++) {
       
            //			if (bitCounter[i] % m != 0) {
       
            //				// 在第i位上有1
            //				ans |= (1 << i);
            //			}
    
            // 题目变一下,如果能找到出现K次的数,就返回该数;如果找不到,返回-1。
            if (bitCounter[i] % m == 0) continue;
            if (bitCounter[i] % m == k) ans |= (1 << i);
            else return -1;
        }
        // 边界处理
        if (ans == 0) {
       
            int cnt = 0;
            for (int x : arr) {
       
                if (x == 0) cnt++;
            }
            if (cnt != k) return -1;
        }
        return ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    3 单双链表、栈和队列、递归和Master公式、哈希表和有序表的使用和性能

    • 内容:
      • 单链表、双链表
      • 栈、队列
      • 递归的物理实质
      • 评估递归复杂度的Master公式
      • 哈希表的使用和性能
      • 有序表的使用和性能
    3.1 反转单链表、反转双链表
    • 反转单链表
    image.png
    • 反转双链表
    image.png
    3.2 在链表中删除指定值的所有节点
    public static Node removeValue(Node head, int num) {
       
        // head来到第一个不需要删的位置
        while (head != null) {
       
            if (head.val != num) break;
            head = head.next;
        }
        // 1 ) head == null
        // 2 ) head != null
        Node pre = head;
        Node cur = head;
        while (cur != null) {
       
            if (cur.val == num) pre.next = cur.next;
            else pre = cur;
            cur = cur.next;
        }
        return head;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    3.3 用双链表实现栈和队列
    • 基于之前实现的双端队列 MyDeque 实现
    public static class MyStack<T> {
       
        private MyDeque<T> queue;
    
        public MyStack() {
       
            queue = new MyDeque<>();
        }
        public void push(T value) {
       
            queue.pushHead(value);
        }
        public T pop() {
       
            return queue.pollHead();
        }
        public boolean isEmpty() {
       
            return queue.isEmpty();
        }
    }
    
    public static class MyQueue<T> {
       
        private MyDeque<T> queue;
    
        public MyQueue() {
       
            queue = new MyDeque<>();
        }
        public void push(T value) {
       
            queue.pushHead(value);
        }
        public T poll() {
       
            return queue.pollTail();
        }
        public boolean isEmpty() {
       
            return queue.isEmpty();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    3.4 用环形数组实现栈和队列
    public static class MyQueue {
       
        private int[] items;
        private int head;
        private int tail;
        private int size;
        private final int capacity;
    
        public MyQueue(int capacity) {
       
            this.items = new int[capacity];
            this.head = 0;
            this.tail = 0;
            this.size = 0;
            this.capacity = capacity;
        }
    
        public void push(int val) {
       
            if (isFull()) throw new RuntimeException("queue is full");
            size++;
            items[head] = val;
            head = resetOffset(head);
        }
    
        public int pop() {
       
            if (isEmpty()) throw new RuntimeException("queue is empty");
            size--;
            int item = items[tail];
            tail = resetOffset(tail);
            return item;
        }
    
        public boolean isEmpty() {
       
            return size == 0;
        }
      
        public boolean isFull() {
       
            return size == capacity;
        }
    
        private int resetOffset(int idx) {
       
            return idx < capacity - 1 ? idx + 1 : 0;
        } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    3.5 实现有getMin功能的栈
    public static class MinStack {
       
        // 定义两个栈:一个数据栈(用于正常存放数据)、一个最小栈(用于存放栈中的最小值)
        private Stack<Integer> dataStack;
        private Stack<Integer> minStack;
        public MinStack() {
       
            dataStack = new Stack<>();
            minStack = new Stack<>();
        }
        public void push(int newVal) {
       
            dataStack.push(newVal);
            // 每次push新值时,同时维护最小栈minStack
            // minStack如果为空,则直接放进去;如果不为空,则比较当前栈顶最小值和新值,取小的
            minStack.push(minStack.isEmpty() ? newVal : Math.min(newVal, minStack.peek()));
        }
        public int pop() {
       
            if (dataStack.isEmpty()) throw new RuntimeException("MinStack is Empty");
            int val = dataStack.pop();
            // 每次pop时,比较是否是栈顶的最小值,如果是,则minStack也需要出栈
            if (val == minStack.peek()) minStack.pop();
            return val;
        }
        public int getMin() {
       
            if (minStack.isEmpty()) throw new RuntimeException("MinStack is Empty");
            return minStack.peek();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    3.6 两个栈实现队列
    public static class StackQueue<T> {
       
        // 定义两个栈:一个push栈(作为队列头部加数据)、一个pop栈(作为队列尾部取数据)
        private Stack<T> pushStack;
        private Stack<T> popStack;
        public StackQueue() {
       
            this.pushStack = new Stack<>();
            this.popStack = new Stack<>();
        }
        public void add(T val) {
       
            pushStack.push(val);
            // 每次添加数据时,同时维护pop栈popStack
            // popStack为空时,才将pushStack中的数据导入到popStack
            rebalance();
        }
        public T remove() {
       
            if (isEmpty()) throw new RuntimeException("StackQueue is empty");
            // 每次移除数据时,同时维护pop栈popStack
            rebalance();
            return popStack.pop();
        }
        public T peek() {
       
            if (isEmpty()) throw new RuntimeException("StackQueue is empty");
            // 每次查看数据时,同时维护pop栈popStack
            rebalance();
            return popStack.peek();
        }
        private void rebalance() {
       
            if (!popStack.isEmpty()) return;
            while (!pushStack.isEmpty()) popStack.push(pushStack.pop());
        }
        private boolean isEmpty() {
       
            return popStack.isEmpty() && pushStack.isEmpty();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    3.7 两个队列实现栈
    public static class QueueStack<T> {
       
       // 定义两个队列:一个用于入栈、出栈,一个用于来回倒数据
       private Queue<T> queue;
       private Queue<T> help;
       public QueueStack() {
       
          this.queue = new LinkedList
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    Java版本企业工程项目管理系统平台源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)
    java ssm超市库存管理系统
    线上旧衣回收小程序,隐藏的蓝海回收市场
    spark相关网站
    .NET快速对接极光消息推送
    探索新一代活动获客方式,虚拟化活动棋胜一招 | 厂商征集
    用户忠诚度:小程序积分商城的用户保持方法
    iOS Error Domain=PHPhotosErrorDomain Code=3300
    sgu 176 Flow construction (有源汇的上下界最小流)
    互联网、政务外网、政务专网、政务内网的区别
  • 原文地址:https://blog.csdn.net/yangwei234/article/details/133221269
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号