左神高级进阶班6(利用快排的partition过程、BFPRT、动态规划的斜率优化技巧、二叉树的递归套路、完美洗牌问题)

news2024/11/28 13:32:00

目录

【案例1  利用快排的partition过程,BFPRT】

【题目描述】

 【思路解析】

【代码实现】

【案例2  动态规划的斜率优化技巧】

【题目描述】

【思路解析】

【代码实现】

【案例3  二叉树的递归套路】 

【题目描述】

 【搜索二叉树定义】

 【思路解析】

【代码实现】

【案例4 完美洗牌问题】

【题目描述】​编辑

 【思路解析】

【代码实现】

【案例5 完美洗牌问题的应用】

【题目描述】

【思路解析】

【代码实现】


大家觉得写得可以的话,可以加入QQ群907575059一起讨论算法知识.

【案例1  利用快排的partition过程,BFPRT】

【题目描述】

要求时间复杂度为O(N)。k指的是它排序后在数组中的索引。

 【思路解析】

利用随机快排的思路,随机选择一个数值x。然后将整个无序数组分为小于x的区域,等于x的区域,大于x的区域。

(1)如果k索引落在等与x的区域中,则返回x。

(2)如果k索引小于x的区域的左边界,对小于x的区域进行随机快排的partition过程。

(3)如果k索引大于x的区域的右边界,对大于x的区域进行随机快排的partition过程。

BFPRT

将整个数组分为以五个为一组的小数组,然后求出小数组的中位数,将这些所有小数组的中位数组成一个数组,然后求出这个数组的中位数(求这个数组中位数时,通过递归完成)。根据这个中位数进行随机快排的partition过程,其余过程和上一个方案相似。只是BFPRT在选取分区数值时能保证分区的3个区域较均匀。

 这里只给出BFPRT的代码,两个代码思路相似,并且随机快排实现更为简单。

【代码实现】

package AdvancedPromotion6;


/**
 * @ProjectName: study3
 * @FileName: Ex1
 * @author:HWJ
 * @Data: 2023/9/25 21:51
 */
public class Ex1 {
    public static void main(String[] args) {
       int[] arr = {2,4,7,6,5,3,1,8};
        System.out.println(select(arr,0,arr.length - 1, 0));

    }
    
    public static int select(int[] arr, int begin, int end, int i) {
        if (begin == end) {
            return arr[begin];
        }
        int pivot = medianOfMedians(arr, begin, end);
        int[] pivotRange = partition(arr, begin, end, pivot);
        if (i >= pivotRange[0] && i <= pivotRange[1]) {
            return arr[i];
        } else if (i < pivotRange[0]) {
            return select(arr, begin, pivotRange[0] - 1, i);
        } else {
            return select(arr, pivotRange[1] + 1, end, i);
        }
    }

    public static int medianOfMedians(int[] arr, int begin, int end) {
        int num = end - begin + 1;
        int offset = num % 5 == 0 ? 0 : 1;
        int[] mArr = new int[num / 5 + offset];
        for (int i = 0; i < mArr.length; i++) {
            int beginI = begin + i * 5;
            int endI = beginI + 4;

            // 这里数组长度为5,使用插入排序的时间很低。
            mArr[i] = getMedian(arr, beginI, Math.min(end, endI));
        }
        return select(mArr, 0, mArr.length - 1, mArr.length / 2);
    }

    public static int getMedian(int[] arr, int i, int j) {
        insertionSort(arr, i, j);
        return arr[(i + j) / 2];
    }

    public static void insertionSort(int[] arr, int begin, int end) {
        for (int i = begin + 1; i != end + 1; i++) {
            for (int j = i; j != begin; j--) {
                if (arr[j - 1] > arr[j]) {
                    swap(arr, j - 1, j);
                } else {
                    break;
                }
            }
        }
    }

    // 返回等于区域的左右边界
    public static int[] partition(int[] arr, int begin, int end, int num) {
        int p1 = begin - 1;
        int p2 = end + 1;
        int index = begin;
        int[] data = new int[2];
        while (index < p2) {
            if (arr[index] < num) {
                swap(arr, index++, ++p1);
            } else if (arr[index] > num) {
                swap(arr, index, --p2);
            } else {
                index++;
            }
        }
        data[0] = p1 + 1;
        data[1] = p2 - 1;
        return data;
    }

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

【案例2  动态规划的斜率优化技巧】

【题目描述】

【思路解析】

因为1 2 1和1 1 2和2 1 1认为是同一种,所以我们就认为每次裂开的数一定要升序(并且第一次裂开的数字一定要大于等于1)。 这样就能保证方法不重复,并且得到所有方法。

通过这样的递归思路可以改为动态规划,使用动态规划的斜率优化技巧。

这里行索引表示先前裂开数,列索引表示当前剩余多少需要裂开。

000000
11ki
101    1j
1 001
1   00001
100001

i表格的值根据上述依赖关系可知,它依赖红色数值。

j表格的值根据依赖关系可知,它依赖蓝色数值。

则i位置可以优化为依赖j位置和k位置。

【代码实现】

package AdvancedPromotion6;


/**
 * @ProjectName: study3
 * @FileName: Ex2
 * @author:HWJ
 * @Data: 2023/9/25 22:52
 */
public class Ex2 {
    public static void main(String[] args) {
        System.out.println(getNum(1, 4));
        System.out.println(dpWay(4));
    }

    // rest表示当前剩下多少需要用来分裂。
    // pre表示之前分裂了多少。
    public static int getNum(int pre, int rest){
        if (rest == 0){
            return 1;
        }
        if (pre > rest){
            return 0;
        }
        int res = 0;
        for (int i = pre; i <= rest; i++) {
            res += getNum(i, rest - i);
        }
        return res;
    }

    public static int dpWay(int num){
        if (num < 1) {
            return 0;
        }
        int[][] map = new int[num + 1][num + 1];
        for (int i = 1; i < num + 1; i++) {
            map[i][0] = 1;
        }
        for (int i = 1; i < num + 1; i++) {
            map[i][i] = 1;
        }
        for (int i = num - 1; i > 0; i--) {
            for (int j = i + 1; j < num + 1; j++) {
                map[i][j] = map[i][j - i] + map[i + 1][j];
            }
        }
        return map[1][num];
    }
    
}

【案例3  二叉树的递归套路】 

【题目描述】

 【搜索二叉树定义】

搜索二叉树(Binary Search Tree,BST)是一种二叉树,其中每个节点都包含一个关键字,且每个节点的关键字大于其左子树中任何节点的关键字,小于其右子树中任何节点的关键字。也就是说,对于搜索二叉树中的任何一个节点,该节点左子树中所有关键字的值都小于该节点关键字的值,同时该节点右子树中所有关键字的值都大于该节点关键字的值。这种排序方式使得搜索二叉树可以用来进行快速的查找、插入、删除等操作。

 【思路解析】

 因为我们要返回满足搜索二叉树条件的最大拓扑结构的大小。因为拓扑结构不是子树,即它可以丢弃某些子树,来使这棵树满足搜索二叉树的条件。所以我们可以从下至上更新拓扑结构。

【代码实现】

package AdvancedPromotion6;

import java.util.HashMap;

/**
 * @ProjectName: study3
 * @FileName: Ex3
 * @author:HWJ
 * @Data: 2023/9/26 10:31
 */
public class Ex3 {
    public static void main(String[] args) {

    }

    public static class Node {
        Node left;
        Node right;
        int value;

        public Node(int value) {
            this.value = value;
        }
    }

    public static class Record {
        int left;
        int right;

        public Record(int left, int right) {
            this.left = left;
            this.right = right;
        }
    }


    public static int posOrder(Node head, HashMap<Node, Record> map){
        if (head == null){
            return 0;
        }
        int leftAns = posOrder(head.left, map); // 得到左孩子的答案
        int rightAns = posOrder(head.right, map); // 得到右孩子的答案
        modifyMap(head.left, head.value, map, true); // 对左子树进行更新
        modifyMap(head.right, head.value, map, false);  // 对右子树进行更新
        Record left = map.get(head.left);
        Record right = map.get(head.right);
        int lBest = left == null ? 0 : left.left + left.right + 1;
        int rBest = right == null ? 0 : right.right + right.left + 1;
        Record record = new Record(lBest, rBest);
        map.put(head, record);
        return Math.max(lBest + rBest + 1, Math.max(leftAns, rightAns));
    }

    // s == true, 查询左子树的右边界    s == false, 查询右子树的左边界。
    public static int modifyMap(Node node, int value, HashMap<Node, Record> map, boolean s) {
        if (node == null || !map.containsKey(node)) {
            return 0;
        }
        Record record = map.get(node);
        if ((s && node.value > value) || (!s && node.value < value)) {
            map.remove(node);
            return record.left + record.right + 1; // 传回信息,让它上面的结点更新表结构。
            // 如果第一次进来就不满足搜索二叉树条件,则直接删除结构,然后在调用处进行更新表结构。
        } else {
            int minus = modifyMap(s ? node.right : node.left, value, map, s);
            if (s) {
                record.right = record.right - minus;
            } else {
                record.left -= minus;
            }
            map.put(node, record); // 更新表结构
            return minus; // 传回信息,让它上面的结点更新表结构。
        }

    }
}

【案例4 完美洗牌问题】

【题目描述】

 【思路解析】

因为 这个数组位置的交换是很明确的,即我们在一开始就可以通过简单的函数来确定它最后应该到达的位置,假设索引位置从1开始,则左边区域的i位置最后会去到2i位置,则右边区域的j位置最后会去到2*(j-n)-1位置。(最后所有索引可以统一为(2*i)%(len + 1))。我们最原始的想法,它能不能像多米诺骨牌一样一直推下去(通过得到它最后要去的索引位置实现),但是我们发现有些数组长度的能满足这样的效应,但是有些数组长度,我们会发现它会形成几个多米诺骨牌一样的圈。

神级结论:

下面我要引用此论文“A Simple In-Place Algorithm for In-Shuffle”的一个结论了, 即,对于2*n = (3^k-1)这种长度的数组,恰好只有k个圈,且每个圈头部的起始位置分别是1,3,9,...3^(k-1)。

这里是论文:

然后我们考虑一个事:

我们怎样将

a b c d 1 2 3 ->> 改为 1 2 3 a b c d;

第一步两边逆序, d c b a 3 2 1

第二步整体逆序 1 2 3 a b c d.

根据结论我们可以得到这些特殊数组长度的有效解法,但是对于一般偶数,我们无有效解。但是我们可以把他们分为特殊长度。假设长度为 14,我们可以分为8 2 2 2.

并且可以通过多次逆序实现 L1 L2 L4 L4 R1 R2 R3 R4 L5 R5 L6 R6 L6 R7。然后对这些·特殊数组进行完美洗牌即可。

【代码实现】

package AdvancedPromotion6;

/**
 * @ProjectName: study3
 * @FileName: Ex4
 * @author:HWJ
 * @Data: 2023/9/26 11:22
 */
public class Ex4 {
    public static void main(String[] args) {

    }

    public static int modifyIndex(int i, int len) {
        if (len <= len / 2) {
            return 2 * i;
        } else {
            return 2 * (i - len) - 1;
        }
    }

    public static int modifyIndex2(int i, int len) {
        return (2 * i) % (len + 1);
    }

    public static void shuffle(int[] arr) {

        // (arr.length & 1) == 0限制数组长度不能为奇数。
        if (arr != null && arr.length != 0 && (arr.length & 1) == 0) {
            shuffle(arr, 0, arr.length - 1);
        }
    }

    public static void shuffle(int[] arr, int l, int r) {
        while (r - l + 1 > 0) {
            int len = r - l + 1;
            int base = 3;
            int k = 1;

            // base <= (len + 1) / 3,这样的循环条件能保证循环终止后,3^(k-1) - 1 <= len中最大的
            while (base <= (len + 1) / 3) {
                base *= 3;
                k++;
            }
            int half = (base - 1) / 2;
            int mid = (l + r) / 2;
            rotate(arr, l + half, mid,  mid + 1, mid + half);
            cycles(arr, l, base - 1, k);
            l = l + base - 1;
        }
    }

    public static void rotate(int[] arr, int p1, int p2, int q1, int q2) {
        reverse(arr, p1, p2);
        reverse(arr, q1, q2);
        reverse(arr, p1, q2);
    }

    public static void reverse(int[] arr, int p, int q) {
        while (p < q) {
            int tmp = arr[p];
            arr[p++] = arr[q];
            arr[q--] = tmp;
        }
    }

    public static void cycles(int[] arr, int start, int len, int k){
        for (int i = 0, trigger = 1; i < k; i++, trigger *= 3) {
            int preValue = arr[start + trigger - 1];
            int cur = modifyIndex2(trigger, len);
            while (cur != trigger){
                int tmp = arr[start + cur - 1];
                arr[start + cur - 1] = preValue;
                preValue = tmp;
                cur = modifyIndex2(cur, len);
            }
            arr[cur + start - 1] = preValue;
        }
    }
}

【案例5 完美洗牌问题的应用】

【题目描述】

给定一个无序数组,如何在空间复杂度为O(1)的要求下,

将数组改为满足a[0] <= a[1],a[1]>=a[2],a[2] <=a[3],a[3]>=a[4].......的顺序。

【思路解析】

因为要求在空间复杂度为O(1)的情况下完成,所以只能使用堆排序。

使用堆排序后整个数组变成整体递增的。

(1)如果数组长度为偶数,L1 L2 L3 L4 R1 R2 R3 R4.

使用完美洗牌后变为 R1 L1 R2 L2 R3 L3 R4 L4.整体为 >= <= >= <=。

但是如果每两个为一组,则可以实现。

(2)如果数组长度为奇数,

L0 L1 L2 L3 L4 R1 R2 R3 R4.除去第一个,使用完美洗牌。

变为L0 R1 L1 R2 L2 R3 L3 R4 L4.整体为<= >= <= >= <=满足题目要求。

【代码实现】

堆排序请看看博客详解堆排序和桶排序和排序大总结_Studying~的博客-CSDN博客

package class05;

import java.util.Arrays;

public class Problem04_ShuffleProblem {

	// 数组的长度为len,调整前的位置是i,返回调整之后的位置
	// 下标不从0开始,从1开始
	public static int modifyIndex1(int i, int len) {
		if (i <= len / 2) {
			return 2 * i;
		} else {
			return 2 * (i - (len / 2)) - 1;
		}
	}

	// 数组的长度为len,调整前的位置是i,返回调整之后的位置
	// 下标不从0开始,从1开始
	public static int modifyIndex2(int i, int len) {
		return (2 * i) % (len + 1);
	}

	// 主函数
	// 数组必须不为空,且长度为偶数
	public static void shuffle(int[] arr) {
		if (arr != null && arr.length != 0 && (arr.length & 1) == 0) {
			shuffle(arr, 0, arr.length - 1);
		}
	}

	// 在arr[L..R]上做完美洗牌的调整
	public static void shuffle(int[] arr, int L, int R) {
		while (R - L + 1 > 0) { // 切成一块一块的解决,每一块的长度满足(3^k)-1
			int len = R - L + 1;
			int base = 3;
			int k = 1;
			// 计算小于等于len并且是离len最近的,满足(3^k)-1的数
			// 也就是找到最大的k,满足3^k <= len+1
			while (base <= (len + 1) / 3) {
				base *= 3;
				k++;
			}
			// 当前要解决长度为base-1的块,一半就是再除2
			int half = (base - 1) / 2;
			// [L..R]的中点位置
			int mid = (L + R) / 2;
			// 要旋转的左部分为[L+half...mid], 右部分为arr[mid+1..mid+half]
			// 注意在这里,arr下标是从0开始的
			rotate(arr, L + half, mid, mid + half);
			// 旋转完成后,从L开始算起,长度为base-1的部分进行下标连续推
			cycles(arr, L, base - 1, k);
			// 解决了前base-1的部分,剩下的部分继续处理
			L = L + base - 1;
		}
	}

	// 从start位置开始,往右len的长度这一段,做下标连续推
	// 出发位置依次为1,3,9...
	public static void cycles(int[] arr, int start, int len, int k) {
		// 找到每一个出发位置trigger,一共k个
		// 每一个trigger都进行下标连续推
		// 出发位置是从1开始算的,而数组下标是从0开始算的。
		for (int i = 0, trigger = 1; i < k; i++, trigger *= 3) {
			int preValue = arr[trigger + start - 1];
			int cur = modifyIndex2(trigger, len);
			while (cur != trigger) {
				int tmp = arr[cur + start - 1];
				arr[cur + start - 1] = preValue;
				preValue = tmp;
				cur = modifyIndex2(cur, len);
			}
			arr[cur + start - 1] = preValue;
		}
	}

	// [L..M]为左部分,[M+1..R]为右部分,左右两部分互换
	public static void rotate(int[] arr, int L, int M, int R) {
		reverse(arr, L, M);
		reverse(arr, M + 1, R);
		reverse(arr, L, R);
	}

	// [L..R]做逆序调整
	public static void reverse(int[] arr, int L, int R) {
		while (L < R) {
			int tmp = arr[L];
			arr[L++] = arr[R];
			arr[R--] = tmp;
		}
	}

	public static void wiggleSort(int[] arr) {
		if (arr == null || arr.length == 0) {
			return;
		}
		// 假设这个排序是额外空间复杂度O(1)的,当然系统提供的排序并不是,你可以自己实现一个堆排序
		Arrays.sort(arr);
		if ((arr.length & 1) == 1) {
			shuffle(arr, 1, arr.length - 1);
		} else {
			shuffle(arr, 0, arr.length - 1);
			for (int i = 0; i < arr.length; i += 2) {
				int tmp = arr[i];
				arr[i] = arr[i + 1];
				arr[i + 1] = tmp;
			}
		}
	}

	// for test
	public static boolean isValidWiggle(int[] arr) {
		for (int i = 1; i < arr.length; i++) {
			if ((i & 1) == 1 && arr[i] < arr[i - 1]) {
				return false;
			}
			if ((i & 1) == 0 && arr[i] > arr[i - 1]) {
				return false;
			}
		}
		return true;
	}

	// for test
	public static void printArray(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	// for test
	public static int[] generateArray() {
		int len = (int) (Math.random() * 10) * 2;
		int[] arr = new int[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (int) (Math.random() * 100);
		}
		return arr;
	}

	public static void main(String[] args) {
		for (int i = 0; i < 5000000; i++) {
			int[] arr = generateArray();
			wiggleSort(arr);
			if (!isValidWiggle(arr)) {
				System.out.println("ooops!");
				printArray(arr);
				break;
			}
		}
	}

}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1041949.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BERT 快速理解——思路简单描述

定义&#xff1a; BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一种预训练的语言模型&#xff0c;它基于Transformer架构&#xff0c;通过在大规模的未标记文本上进行训练来学习通用的语言表示。 输入 在BERT中&#xff0c;输入…

一篇博客学会系列(1) —— C语言中所有字符串函数以及内存函数的使用和注意事项

目录 1、求字符串长度函数 1.1、strlen 2、字符串拷贝(cpy)、拼接(cat)、比较(cmp)函数 2.1、长度不受限制的字符串函数 2.1.1、strcpy 2.1.2、strcat 2.1.3、strcmp 2.2、长度受限制的字符串函数 2.2.1、strncpy 2.2.2、strncat 2.2.3、strncmp 3、字符串查找函数…

Java 大厂八股文面试专题-JVM相关面试题 垃圾回收算法 GC JVM调优

Java 大厂八股文面试专题-JVM相关面试题 类加载器_软工菜鸡的博客-CSDN博客 3 垃圾收回 3.1 简述Java垃圾回收机制&#xff1f;&#xff08;GC是什么&#xff1f;为什么要GC&#xff09; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆ 为了让程序员更专注于代码的实现…

如何使用iPhone15在办公室观看家里电脑上的4k电影,实现公网访问本地群晖!

如何使用iPhone15在办公室观看家里电脑上的4k电影&#xff1f; 文章目录 如何使用iPhone15在办公室观看家里电脑上的4k电影&#xff1f;1.使用环境要求&#xff1a;2.下载群晖videostation&#xff1a;3.公网访问本地群晖videostation中的电影&#xff1a;4.公网条件下使用电脑…

【Java基础-JDK21新特性】它发任它发,我用java8

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

黑马JVM总结(二十四)

&#xff08;1&#xff09;练习-分析a a:先执行iload1&#xff1a;把数据读入到操作数栈中 iinc&#xff1a;把局部变量表中的1号曹位做一个自增&#xff0c;他在局部变量表中发生的并没有影响到操作数栈 a&#xff1a;限制性自增在做iload 自增变成12 iload把12读取到操作数…

Mac电脑信息大纲记录软件 OmniOutliner 5 Pro for Mac中文

OmniOutliner 5 Pro是一款专业级的Mac大纲制作工具&#xff0c;它可以帮助用户更好地组织和管理信息&#xff0c;以及制作精美的大纲。以下是OmniOutliner 5 Pro的主要功能和特点&#xff1a; 强大的大纲组织和管理功能。OmniOutliner 5 Pro为用户提供了多层次的大纲结构&…

Python语法之条件语句(很详细)

目录 Python条件语句的介绍 定义 if的语法和实例(最基本的) 语法 gif动态图展示 具体实例 实现思路&#xff1a; if-elif-else的语法和实例&#xff08;最基本的&#xff09; 语法 具体实例 实现思路&#xff1a; 判断需要多个条件需同时判断语法和实例&#xff08;最基…

利用Axure RP和cpolar内网穿透实现公网访问本地web网页

AxureRP制作静态站点发布互联网&#xff0c;内网穿透实现公网访问 文章目录 AxureRP制作静态站点发布互联网&#xff0c;内网穿透实现公网访问前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4…

最详细的next国际化方案

实现效果 : 根据浏览器语言判断和手动切换(两种切换模式) 实现方法 1.下载安装包 (next-i18next react-i18next i18next) yarn add next-i18next react-i18next i18next 2.在根目录下创建文件(next-i18next.config.js) const path require("path");module.expo…

MATLAB 安装额外工具包

接下里即可搜索并安装 “额外工具包”

python多继承构造函数声明问题

前言&#xff1a; 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 背景 有场景分别定义两组逻辑&#xff0c;随后有统一入口做基类属性的整合 其中两组逻辑的积累构造函数定义入参不同 设计类继承图如&#…

【深度学习】【Opencv】Python/C++调用onnx模型【基础】

【深度学习】【Opencv】python/C调用onnx模型【基础】 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【Opencv】python/C调用onnx模型【基础】前言Python版本OpenCVWindows平台安装OpenCVopencv调用onnx模型 C版本OpenCVWindows平…

终极策略:如何利用亮数据代理轻松、高效地突破亚马逊的反爬障碍

文章目录 前言背景&#x1f4dc;第一步&#xff1a;打开亚马逊商城&#x1f6cd;️第二步&#xff1a;定位搜索框并搜索iphone15&#x1f50d;第三步&#xff1a;定位具体数据并保存到csv文件&#x1f4be;第三步&#xff1a;多页面数据抓取&#x1f4c4;&#x1f4c4;&#x1…

OpenCV 基础图像处理

1、生成图像 cv2.imread是OpenCV库中的一个函数&#xff0c;用于读取图像文件。它接受一个参数&#xff0c;即要读取的图像文件的路径&#xff0c;返回一个多维数组&#xff0c; 表示图像的像素值。该函数的常用参数包括&#xff1a;flags&#xff1a;指定读取图像的方式&#…

RabbitMQ学习总结(11)—— RabbitMQ 核心概念与架构

MQ 技术概述 什么是 MQ MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是 message 而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ 是一种非常常见的上下游 “逻辑解耦+物理解耦” 的消息通信服务。使用…

系统集成|第十三章(笔记)

目录 第十三章 干系人管理13.1 概述与相关概念13.2 主要过程13.2.1 识别干系人13.2.2 编制项目干系人管理计划13.2.3 管理干系人参与13.2.4 项目干系人参与的监控 13.3 常见问题 上篇&#xff1a;第十二章、沟通管理 第十三章 干系人管理 13.1 概述与相关概念 概述&#xff1a…

21-SpringSecurity

SpringSecurity从入门到精通 0. 简介 ​ Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 ​ 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有…

会议剪影 | 思腾合力受邀参加2023第二届世界元宇宙大会并作主题演讲

由中国仿真学会、中国指挥与控制学会和北京理工大学共同主办&#xff0c;上海市嘉定区安亭镇人民政府和中国仿真学会元宇宙专业委员会承办的第二届世界元宇宙大会于2023年9月20日-22日在上海安亭举行。 大会以“虚实相生、产业赋能”为主题&#xff0c;聚焦元宇宙关键技术发展的…

Redis原理(一):Redis数据结构(上)

文章目录 1、 Redis数据结构-动态字符串2、 Redis数据结构-intset3、 Redis数据结构-Dict4、 Redis数据结构-ZipList5、 Redis数据结构-ZipList的连锁更新问题6、 Redis数据结构-QuickList1、 Redis数据结构-动态字符串 我们都知道Redis中保存的Key是字符串,value往往是字符串…