后缀数组的应用:[Leetcode] 321.拼接最大数(困难)

news2024/11/9 9:25:02

题目描述

给定长度分别为 mn 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。

求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。

说明: 请尽可能地优化你算法的时间和空间复杂度。

  • 示例 1:
输入:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
输出:
[9, 8, 6, 5, 3]
  • 示例 2:
输入:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
输出:
[6, 7, 6, 0, 4]
  • 示例 3:
输入:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
输出:
[9, 8, 9]

思路分析

整体思路:假设 arr1 中有10个数,arr2 中有8个数,而 k = 5 k=5 k=5,那么有如下几种可能性:

  1. 5个数全部来自arr1,arr2中的数不需要,如何选择才能最大;
  2. 4个数来自arr1,1个数来自arr2,如何merge才能最大;
  3. 3个数来自arr1,2个数来自arr2,如何merge才能最大;
  4. 2个数来自arr1,3个数来自arr2,如何merge才能最大;
  5. 1个数来自arr1,4个数来自arr2,如何merge才能最大;
  6. 0个数来自arr1,5个数全部来自arr2,如何选择才能最大。

最终结果就是所有可能性中的最大值。

于是需要解决两个问题:

  1. 在一个数组中从左往右挑 m m m 个数,怎么挑选才能使得其尽量大?
  2. 从两个数组中挑选出来的数怎么merge才能最大?

解决问题1:在一个数组中从左往右挑 m m m 个数,怎么挑选才能使得其尽量大?

因为要在数组中可能挑不同数量的数多次,所以要先解决一个批量查询的问题,先预处理一个结构,之后不管是在数组挑几个数,从这个预处理结构中都能很快得到答案,也就是说「解决批量查询,做一个预处理结构」。

准备一张 dp 表,行表示数组的下标,列表示个数,定义 dp[i][j] 表示只能从arr数组的 i i i 位置及其往后的位置中挑选 j j j 个数,最大结果的开始位置

比如数组 [6, 7, 4, 9, 2],dp[2][1] = 3,因为从数组2位置下标开始只能选1个数,最大的就是3位置的9,所以 dp[2][1] = 3

在这里插入图片描述
按照填表规则“先填对角线,然后从左往右,每列从下往上填”,那么当要填 dp[i][j] 时,dp[i+1][j] 是已经填好的了,如何使用 dp[i+1][j] 指导 dp[i][j] 的填写?

假设现在要填 dp[7][3],则之前 dp[8][3] 已经填写过,若dp[8][3] = 13,那么只需要比较 7 位置的数和 13 位置的数,如果arr[7] > arr[13],则dp[7][3] = 7;如果 arr[7] < arr[13],则 dp[7][3] = 13。关键是如果 arr[7] = arr[13],应该怎么处理?注意相等的时候一定要选7,即dp[7][3] = 7,虽然相等情况下,第一个数无论是7位置还是13位置都是相同的,但是选择7位置开始后,可以让接下来的第二个数也尽量好。

举个例子:假如从8位置开始往后选3个数,得到的最大结果是996,而因为7位置的数和13位置是相同的,所以7位置的数也是9,那么从7位置选三个数,可以得到999,即13位置的9变成了第二个9。所以当7位置的数和13位置的数相同的时候,一定要选择7位置的数,这样会使得后面的结果潜在地变好。

填第1列:
在这里插入图片描述
补充填完整个表:
在这里插入图片描述
这张表就是预处理结构,可以获得数组arr中挑任意个数,怎么挑最大。

比如「如果要在数组 arr 中挑 2 个数,如何选择使得拼接的结果最大?」

根据表中信息已经知道「从0位置出发挑2个数」最好的开始位置是1,所以arr[1] = 9 要选择;然后问题就变成了「从2位置出发挑1个数」,而根据表中信息,dp[2][1] = 3,所以arr[3] = 9要选择。因此,最后「arr中挑2个数拼接的最大结果为99」。

这个预处理结构可以处理任意情况,比如 『要在 arr 中挑选 6 个数,怎么挑最大?』,其实质就是将问题分解成了如下步骤:

  1. 从0开始挑6个数的最开始位置,假设为7,则第一个要选择的数就是 arr[7];
  2. 然后问题变成了从 8 位置开始挑 5 个数的最开始位置,假设为13,则第二个要选择的数就是 arr[13];
  3. 然后问题变成了从 14 位置开始挑 4 个数的最开始位置,假设为19,则第三个要选择的数就是 arr[19];
  4. 然后问题变成了从 20 位置开始挑 3 个数的最开始位置,假设为25,则第四个要选择的数就是 arr[25];
  5. 然后问题变成了从 26 位置开始挑 2 个数的最开始位置,假设为30,则第五个要选择的数就是 arr[30];
  6. 以此类推,这些信息都可以在表中获得。

如果要在数组中选 m m m 个数,只需要在表中跳转 m m m 次,时间复杂度为 O ( m ) O(m) O(m)

解决问题2:从两个数组中挑选出来的数怎么merge才能最大?

【没有优化的merge方法】

假设 k = 8 k=8 k=8,已经从两个数组中分别将数挑选出来:[9, 9, 9, 3, 2],[9, 9, 4]

一开始,准备两个指针,各自指向数组的开头,如下:
在这里插入图片描述
依次比较两个数组中的值,直到比较出大小。即:

  • arr1[0] = arr2[0],没有分出大小,继续比较后一个位置;
  • arr1[1] = arr2[1],没有分出大小,继续比较后一个位置;
  • arr1[2] > arr2[2],分出了大小。

于是将arr1中此时指针指向的9作为答案的第一位:
在这里插入图片描述
然后 arr1 的指针后移一位:
在这里插入图片描述
继续依次比较,直到分出大小,即:

  • arr1[1] = arr2[0],没分出大小,继续比较后一个位置;
  • arr1[2] = arr2[1],没分出大小,继续比较;
  • arr1[3] < arr2[2],分出大小。

于是选择arr2此时指针指向的9 作为结果的第二位:
在这里插入图片描述
然后 arr2 的指针后移:
在这里插入图片描述
继续重复从指针开始的位置依次比较的操作,直到分出大小。

总结来说就是从两个数组指针指向的位置的值相等时,必须依次比较,直到比较出大小,然后取较大的那个值所在数组中当前指针指向的值,然后将该数组的指针后移。这个操作的时间复杂度是平方规模的。

注意,在相等时不能随意选一个,而是必须像上述操作一样,依次比较直到区分出大小为止。比如 [5, 9] 和 [5, 6] 合并,一开始两个数组的第一个位置相等,如果直接选择了第二个数组的5,那么最终得到的结果是 5659 而不是正确的 5956。

【优化过的merge方法】

上述的操作中,其实就是在找两个数组当前指针所指向的位置开始的后缀串的字典序,比较这两个字典序的大小,取字典序较大的数组中当前指针指向的值。

而此前已经将两个数组合并为一个数组利用DC3算法求出了后缀数组,所以两个数组当前指针指向的位置的字典序大小根据后缀数组能很轻易地得到。

举个例子来说明整体流程:arr1 = [3, 1, 2, 6], arr2 = [6, 9],两个数组中的每个值都+2,然后用1隔开,则合并成一个数组是 [5, 3, 4, 8, 1, 9, 11],将该数组利用DC3算法求出后缀数组,然后比较原数组对应到该合成数组中的下标位置,利用字典序的大小进行合并。

代码实现

// 测试链接: https://leetcode.com/problems/create-maximum-number/
public class CreateMaximumNumber {

	public static int[] maxNumber1(int[] nums1, int[] nums2, int k) {
		int len1 = nums1.length;
		int len2 = nums2.length;
		if (k < 0 || k > len1 + len2) {
			return null;
		}
		int[] res = new int[k];
		int[][] dp1 = getdp(nums1); // 生成dp1这个表,以后从nums1中,只要固定拿N个数,
		int[][] dp2 = getdp(nums2);
		
		// get1表示从arr1里拿的数量
		// K - get1表示从arr2里拿的数量
		
		// 如果arr1中有10个数,arr2中有8个数,而k=5,则可以依据前文的方案枚举可能性:
		// ① arr1 选5个,arr2选0个;
		// ② arr1 选4个,arr2选1个;
		// ③ arr1 选3个,arr2选2个;
		// ④ arr1 选2个,arr2选3个;
		// ⑤ arr1 选1个,arr2选4个;
		// ⑥ arr1 选0个,arr2选5个。
		 
		// 但是如果 arr1 中只有4个数,arr2中只有3个数,而k=5,则需要根据这几个值的关系定制方案:
		// ① arr1 选4个,arr2 选1个;
		// ② arr1 选3个,arr2 选2个;
		// ③ arr1 选2个,arr2 选3个;
		
		//如果arr1中有 N 个数,arr2中有 M 个数,选择 K 个数
		//若M < K,则在arr1中至少要选择K-M个数,而 M>=K 的时候,在arr1中最少可以选择0个
		// 若N < K,则在arr1中最多选择N个数;若N >= K, 在arr1中最多选择K个数
		//所以在arr1中可以挑选的数的个数范围为 [max{0, K-M}, min{K, N}]
		for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) {
			int[] pick1 = maxPick(nums1, dp1, get1); // arr1 挑 get1个,怎么得到一个最优结果
			int[] pick2 = maxPick(nums2, dp2, k - get1); //arr2 挑 k-get1个,怎么得到一个最优结果
			int[] merge = merge(pick1, pick2); //从arr1和arr2中挑选出来的数怎么合并结果最优
			res = preMoreThanLast(res, 0, merge, 0) ? res : merge;
		}
		return res;
	}
	
	//没有优化的merge
	public static int[] merge(int[] nums1, int[] nums2) {
		int k = nums1.length + nums2.length;
		int[] ans = new int[k];
		for (int i = 0, j = 0, r = 0; r < k; ++r) {
			ans[r] = preMoreThanLast(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];
		}
		return ans;
	}

	public static boolean preMoreThanLast(int[] nums1, int i, int[] nums2, int j) {
		while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) {
			i++;
			j++;
		}
		return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]);
	}

	public static int[] maxNumber2(int[] nums1, int[] nums2, int k) {
		int len1 = nums1.length;
		int len2 = nums2.length;
		if (k < 0 || k > len1 + len2) {
			return null;
		}
		int[] res = new int[k];
		int[][] dp1 = getdp(nums1);
		int[][] dp2 = getdp(nums2);
		for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) {
			int[] pick1 = maxPick(nums1, dp1, get1);
			int[] pick2 = maxPick(nums2, dp2, k - get1);
			int[] merge = mergeBySuffixArray(pick1, pick2);
			res = moreThan(res, merge) ? res : merge;
		}
		return res;
	}

	public static boolean moreThan(int[] pre, int[] last) {
		int i = 0;
		int j = 0;
		while (i < pre.length && j < last.length && pre[i] == last[j]) {
			i++;
			j++;
		}
		return j == last.length || (i < pre.length && pre[i] > last[j]);
	}

	//优化版本的merge
	public static int[] mergeBySuffixArray(int[] nums1, int[] nums2) {
		int size1 = nums1.length;
		int size2 = nums2.length;
		int[] nums = new int[size1 + 1 + size2]; //合并成一个数组
		for (int i = 0; i < size1; i++) {
			nums[i] = nums1[i] + 2;
		}
		nums[size1] = 1;
		for (int j = 0; j < size2; j++) {
			nums[j + size1 + 1] = nums2[j] + 2;
		}
		DC3 dc3 = new DC3(nums, 11);
		int[] rank = dc3.rank;
		int[] ans = new int[size1 + size2];
		int i = 0;
		int j = 0;
		int r = 0;
		while (i < size1 && j < size2) {
			//通过rank数组能直接知道哪个字典序更大
			ans[r++] = rank[i] > rank[j + size1 + 1] ? nums1[i++] : nums2[j++];
		}
		while (i < size1) {
			ans[r++] = nums1[i++];
		}
		while (j < size2) {
			ans[r++] = nums2[j++];
		}
		return ans;
	}

	public static class DC3 {

		public int[] sa;

		public int[] rank;

		public DC3(int[] nums, int max) {
			sa = sa(nums, max);
			rank = rank();
		}

		private int[] sa(int[] nums, int max) {
			int n = nums.length;
			int[] arr = new int[n + 3];
			for (int i = 0; i < n; i++) {
				arr[i] = nums[i];
			}
			return skew(arr, n, max);
		}

		private int[] skew(int[] nums, int n, int K) {
			int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;
			int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];
			for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {
				if (0 != i % 3) {
					s12[j++] = i;
				}
			}
			radixPass(nums, s12, sa12, 2, n02, K);
			radixPass(nums, sa12, s12, 1, n02, K);
			radixPass(nums, s12, sa12, 0, n02, K);
			int name = 0, c0 = -1, c1 = -1, c2 = -1;
			for (int i = 0; i < n02; ++i) {
				if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {
					name++;
					c0 = nums[sa12[i]];
					c1 = nums[sa12[i] + 1];
					c2 = nums[sa12[i] + 2];
				}
				if (1 == sa12[i] % 3) {
					s12[sa12[i] / 3] = name;
				} else {
					s12[sa12[i] / 3 + n0] = name;
				}
			}
			if (name < n02) {
				sa12 = skew(s12, n02, name);
				for (int i = 0; i < n02; i++) {
					s12[sa12[i]] = i + 1;
				}
			} else {
				for (int i = 0; i < n02; i++) {
					sa12[s12[i] - 1] = i;
				}
			}
			int[] s0 = new int[n0], sa0 = new int[n0];
			for (int i = 0, j = 0; i < n02; i++) {
				if (sa12[i] < n0) {
					s0[j++] = 3 * sa12[i];
				}
			}
			radixPass(nums, s0, sa0, 0, n0, K);
			int[] sa = new int[n];
			for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {
				int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
				int j = sa0[p];
				if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3])
						: leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {
					sa[k] = i;
					t++;
					if (t == n02) {
						for (k++; p < n0; p++, k++) {
							sa[k] = sa0[p];
						}
					}
				} else {
					sa[k] = j;
					p++;
					if (p == n0) {
						for (k++; t < n02; t++, k++) {
							sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;
						}
					}
				}
			}
			return sa;
		}

		private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {
			int[] cnt = new int[k + 1];
			for (int i = 0; i < n; ++i) {
				cnt[nums[input[i] + offset]]++;
			}
			for (int i = 0, sum = 0; i < cnt.length; ++i) {
				int t = cnt[i];
				cnt[i] = sum;
				sum += t;
			}
			for (int i = 0; i < n; ++i) {
				output[cnt[nums[input[i] + offset]]++] = input[i];
			}
		}

		private boolean leq(int a1, int a2, int b1, int b2) {
			return a1 < b1 || (a1 == b1 && a2 <= b2);
		}

		private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {
			return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));
		}

		private int[] rank() {
			int n = sa.length;
			int[] ans = new int[n];
			for (int i = 0; i < n; i++) {
				ans[sa[i]] = i;
			}
			return ans;
		}

	}

	public static int[][] getdp(int[] arr) {
		int size = arr.length; // 0~N-1
		int pick = arr.length + 1; // 1 ~ N
		int[][] dp = new int[size][pick];
		// get 不从0开始,因为拿0个无意义
		for (int get = 1; get < pick; get++) { // 1 ~ N
			int maxIndex = size - get;
			// i~N-1
			for (int i = size - get; i >= 0; i--) {
				if (arr[i] >= arr[maxIndex]) {
					maxIndex = i;
				}
				dp[i][get] = maxIndex;
			}
		}
		return dp;
	}

	public static int[] maxPick(int[] arr, int[][] dp, int pick) {
		int[] res = new int[pick];
		for (int resIndex = 0, dpRow = 0; pick > 0; pick--, resIndex++) {
			res[resIndex] = arr[dp[dpRow][pick]];
			dpRow = dp[dpRow][pick] + 1;
		}
		return res;
	}
}

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

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

相关文章

ChatGPT 究竟在做什么?它为何能做到这些?(1)

ChatGPT能够自动生成一些表面上看起来像人类写出的文字的东西&#xff0c;是一件很厉害且出乎大家意料的事。那么&#xff0c;它是如何做到的呢&#xff1f;又是为何能做到呢&#xff1f;我在这里想大致介绍一下ChatGPT的内部机理&#xff0c;然后探讨一下为什么它能很好地生成…

ZNS 架构实现 : 解决传统SSD问题的高性能存储栈设计

声明 主页&#xff1a;元存储的博客_CSDN博客 依公开知识及经验整理&#xff0c;如有误请留言。 个人辛苦整理&#xff0c;付费内容&#xff0c;禁止转载。 内容摘要 2.2 ZNS 的架构实现 先看看 支持zone 存储的 SMR HDD 以及 支持 zonefs 的 nvme ssd 的整个存储栈形态 其中对…

前端项目-12-个人中心-二级路由配置-导航守卫-懒加载

目录 1-个人中心 1.1-个人中心路由注册 1.2-拆分二级路由组件 1.3-动态渲染我的订单页面 2-导航守卫优化 2.1-用户未登录导航守卫优化 2.2-路由独享 2.3-组件内守卫 3-懒加载 3.1-图片懒加载 3.2-路由懒加载 4-map文件处理 1-个人中心 需求&#xff1a;当用户点击支…

计算机图形学 | 实验五:模型导入

计算机图形学 | 实验五&#xff1a;模型导入计算机图形学 | 实验五&#xff1a;模型导入模型加载库AssimpAssimp简介Assimp构建Mesh && Model 类的创建MeshModel绘制模型华中科技大学《计算机图形学》课程 MOOC地址&#xff1a;计算机图形学&#xff08;HUST&#xff…

进阶C语言

1.数据的存储 1.1 为什么数据在内存中存放的是补码 因为CPU只有加法器,而使用补码&#xff0c;就可以将符号位和数值域统一处理(即统一处理加法和减法)且不会需要额外的硬件电路。 1.2 为什么会有大小端 这是因为在计算机系统中,是以字节为单位的,比如: 每个地址单元都对应着…

双指针算法初阶

前言&#xff1a;首先&#xff0c;这是不是你所了解的双指针算法&#xff1f; for (int i 0; i < n; i) {for (int j 0; j < n; j){...} } 那你就要继续往下看了&#xff0c;双指针算法可不是简单的两层的for循环暴力&#xff0c;这并不能起到时间优化的作用。 那话…

基于html+css的图片展示9

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

educoder实训——数值类型

第1关:三角形周长及面积 任务描述 输入的三角形的三条边a、b、c 的长度,计算并依次输出三角形的周长和面积,结果严格保留2位小数。测试用例的数据保证三角形三边数据可以构成三角形。 三角形面积计算公式: 其中s=(a+b+c)/2。 输入格式 分三行输入 3 个浮点数,表示三…

银行数字化转型导师坚鹏:金融数字化转型助力乡村振兴及案例

金融数字化转型助力乡村振兴及案例课程背景&#xff1a; 很多银行存在以下问题&#xff1a; 不清楚如何借助数字化转型助力乡村振兴&#xff1f; 不知道普惠金融模式和产品如何有效创新&#xff1f; 不知道数字化转型助力乡村振兴的成功案例&#xff1f; 课程特色&#xff1…

【用AI写周报,“卷死”同事】打造一款自动生成周报的微信小程序

文章目录前言步骤1&#xff1a;创建一个ChatGPT账号步骤2&#xff1a;创建一个微信小程序并配置API。步骤3&#xff1a;在微信开发者工具中创建一个新的微信小程序项目步骤4&#xff1a;创建ChatGPT API云函数步骤5&#xff1a;创建UI界面步骤6&#xff1a;创建发送邮件的云函数…

Kubernetes 1.27 正式发布

Kubernetes 1.27 正式发布&#xff0c;这是 2023 年的第一个版本。这个版本包括 60 项增强功能。其中 18 项增强功能进入 Alpha、29 项进入 Beta&#xff0c;还有 13 项升级为 Stable 稳定版。 主题和标识 Kubernetes v1.27 的主题是 Chill Vibes 新内容 冻结 k8s.gcr.io镜像…

replugin宿主与插件通信小结

近来replugin开发中遇到宿主和插件间需要通信的情形&#xff0c;思来只有进程间通信(IPC)才是比较好的宿主与插件的通信方式。而Android进程间通信主要有2种方式&#xff1a;Messenger和AIDL。 AIDL&#xff08;Android Interface Definition Language&#xff09;是Android接…

矩阵和线性代数的应用

矩阵和线性代数是数学中重要的概念&#xff0c;它们被广泛应用于物理、工程、计算机科学、经济学等众多领域。本文将讨论矩阵和线性代数的一些基本概念以及它们在实际应用中的重要性和影响。 一、矩阵和线性代数的基本概念 矩阵是由数字组成的矩形数组。它可以表示线性方程组…

线程池并发服务器

线程池技术 线程池技术是一种典型的生产者-消费者模型。 线程池技术是指能够保证所创建的任一线程都处于繁忙状态&#xff0c;而不需要频繁地为了某一任务而创建和销毁线程&#xff0c;因为系统在创建和销毁线程时所耗费的cpu资源很大。如果任务很多&#xff0c;频率很高&am…

Android中级——系统信息与安全机制

系统信息与安全机制系统信息获取/system/build.prop/procandroid.os.buildSystemPropertyPackageManagerActivityManagerpackages.xmlpermissions标签package标签perms标签安全机制Apk反编译apktooldex2jarjd-guiApk加密系统信息获取 /system/build.prop 存放一些配置信息&am…

Seaborn 变量分布分析

文章目录一、数据集1.1 下载数据集1.2 字段含义说明1.3 导入数据集二、初步分析2.1 缺失值分布查看2.2 异常值分布查看2.3 查看变量分布三、数值变量分析3.1 replot()&#xff1a;多个变量之间的关联关系3.2 lmplot()/regplot&#xff1a;分析两个变量的线性关系3.3 displot()&…

从前序与中序遍历序列构造二叉树——力扣105

题目描述 法一&#xff09;递归 复杂度分析 代码如下 class Solution { private:unordered_map<int, int> index;public:TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_ri…

Qt Quick - StackView

StackView 使用总结一、概述二、在应用中使用StackView三、基本的导航1. push Item2. pop Item3. replace Item四、深度链接五、寻找Item六、转换六、Item的所有权七、大小一、概述 StackView可以与一组相互链接的信息页面一起使用。例如&#xff0c;电子邮件应用程序具有单独…

HTML5 <img> 标签

HTML5 <img> 标签 实例 HTML5 <img>标签用于向网页中添加相关图片。 如何插入图像&#xff1a; <img src"smiley-2.gif" alt"Smiley face" width"42" height"42">尝试一下 &#xff08;更多实例见页面底部&…

基于营销类系统运营活动增长带来的数据库设计演进

一、前言 为了支持业务数据的不断增长&#xff0c;在数据库层面的性能提升主要体现在几个维度&#xff1a;1&#xff09;数据降级&#xff1b;2&#xff09;数据主题分而治之&#xff1b;3&#xff09;实时交易转异步&#xff1b;4&#xff09;硬件扩容&#xff0c;当然网上一…