标签:++i 表示 mat ued ble 长度范围 长度 结果 它的
N 对情侣坐在连续排列的 2N 个座位上,想要牵到对方的手。 计算最少交换座位的次数,以便每对情侣可以并肩坐在一起。 一次交换可选择任意两人,让他们站起来交换座位。
人和座位用 0
到 2N-1
的整数表示,情侣们按顺序编号,第一对是 (0, 1)
,第二对是 (2, 3)
,以此类推,最后一对是 (2N-2, 2N-1)
。
这些情侣的初始座位 row[i]
是由最初始坐在第 i 个座位上的人决定的。
示例 1:
输入: row = [0, 2, 1, 3]
输出: 1
解释: 我们只需要交换row[1]和row[2]的位置即可。
示例 2:
输入: row = [3, 2, 0, 1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。
说明:
len(row)
是偶数且数值在 [4, 60]
范围内。row
是序列 0...len(row)-1
的一个全排列。一个数‘异或’上1就是其另一个位,
如果是偶数的话,最后位是0,‘异或’上1等于加了1,变成了可以的成对奇数。
如果是奇数的话,最后位是1,‘异或’上1后变为了0,变成了可以的成对偶数。
class Solution {
public:
int minSwapsCouples(vector<int>& row) {
int res = 0, n = row.size();
for (int i = 0; i < n; i += 2) {
if (row[i + 1] == (row[i] ^ 1)) continue;
++res;
for (int j = i + 1; j < n; ++j) {
if (row[j] == (row[i] ^ 1)) {
row[j] = row[i + 1];
row[i + 1] = row[i] ^ 1;
break;
}
}
}
return res;
}
};
用一个root数组,每个点开始初始化为不同的值,
如果两个点属于相同的组,就将其中一个点的root值赋值为另一个点的位置,这样只要是相同组里的两点,通过find
函数会得到相同的值。
那么如果总共有n个数字,则共有 n/2 对儿,所以我们初始化 n/2 个群组,我们还是每次处理两个数字。
每个数字除以2就是其群组号,那么属于同一组的两个数的群组号是相同的,
比如2和3,其分别除以2均得到1,所以其组号均为1。
那么这对解题有啥作用呢?
由于我们每次取的是两个数,且计算其群组号,并调用find函数,那么如果这两个数的群组号相同,那么find函数必然会返回同样的值,我们不用做什么额外动作,因为本身就是一对儿。
如果两个数不是一对儿,那么其群组号必然不同,在二者没有归为一组之前,调用find函数返回的值就不同,此时我们将二者归为一组,并且cnt
自减1,
cnt
初始化为总群组数,即 n/2。
那么最终cnt
减少的个数就是交换的步数
举例
[3 1 4 0 2 5]
最开始的群组关系是:
群组0:0,1
群组1:2,3
群组2:4,5
取出前两个数字3和1,其群组号分别为1和0,带入find函数返回不同值,则此时将群组0和群组1链接起来,变成一个群组,则此时只有两个群组了,cnt
自减1,变为了2。
群组0 & 1:0,1,2,3
群组2:4,5
此时取出4和0,其群组号分别为2和0,带入find函数返回不同值,则此时将群组0&1和群组2链接起来,变成一个超大群组,cnt
自减1,变为了1。
群组0 & 1 & 2:0,1,2,3,4,5
此时取出最后两个数2和5,其群组号分别为1和2,因为此时都是一个大组内的了,带入find函数返回相同的值,不做任何处理。最终交换的步数就是cnt
减少值,为2
class Solution {
public:
int minSwapsCouples(vector<int>& row) {
int res = 0, n = row.size(), cnt = n / 2;
vector<int> root(n, 0);
for (int i = 0; i < n; ++i) root[i] = i;
for (int i = 0; i < n; i += 2) {
int x = find(root, row[i] / 2);
int y = find(root, row[i + 1] / 2);
if (x != y) {
root[x] = y;
--cnt;
}
}
return n / 2 - cnt;
}
int find(vector<int>& root, int i) {
return (i == root[i]) ? i : find(root, root[i]);
}
};
给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
你找到的子数组应是最短的,请输出它的长度。
示例 1:
输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
说明 :
要确定无序子数组的起始和结束位置,这样就能知道子数组的长度了。
所以我们用一个变量start
来记录起始位置,然后我们开始遍历数组,
当我们发现某个数字比其前面的数字要小的时候,说明此时数组不再有序,所以我们要将此数字向前移动,移到其应该在的地方,
我们用另一个变量j
来记录移动到的位置,然后我们考虑要不要用这个位置来更新start
的值,
当start
还是初始值-1时,肯定要更新,因为这是出现的第一个无序的地方,
还有就是如果当前位置小于start
也要更新,这说明此时的无序数组比之前的更长了。
举个例子来说明,
比如数组[1,3,5,4,2]
,
第一个无序的地方是数字4,它移动到的正确位置是坐标2,此时start
更新为2,
然后下一个无序的地方是数字2,它的正确位置是坐标1,所以此时start应更新为1,
这样每次用i - start + 1
来更新结果res时才能得到正确的结果
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
int res = 0, start = -1, n = nums.size();
for (int i = 1; i < n; ++i) {
if (nums[i] < nums[i - 1]) {
int j = i;
while (j > 0 && nums[j] < nums[j - 1]) {
swap(nums[j], nums[j - 1]);
--j;
}
if (start == -1 || start > j) start = j;
res = i - start + 1;
}
}
return res;
}
};
新建一个跟原数组一摸一样的数组,然后排序。
从数组起始位置开始,两个数组相互比较,当对应位置数字不同的时候停止,
同理再从末尾开始,对应位置上比较,也是遇到不同的数字时停止,这样中间一段就是最短无序连续子数组了
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
int n = nums.size(), i = 0, j = n - 1;
vector<int> t = nums;
sort(t.begin(), t.end());
while (i < n && nums[i] == t[i]) ++i;
while (j > i && nums[j] == t[j]) --j;
return j - i + 1;
}
};
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3
→ 1,3,2
3,2,1
→ 1,2,3
1,1,5
→ 1,5,1
下面一个例子,有如下的一个数组
1 2 7 4 3 1
下一个排列为:
1 3 1 2 4 7
那么是如何得到的呢,我们通过观察原数组可以发现,如果从末尾往前看,数字逐渐变大,到了2时才减小的,然后再从后往前找第一个比2大的数字,是3,那么我们交换2和3,再把此时3后面的所有数字转置一下即可,步骤如下:
1 2 7 4 3 1
1 2 7 4 3 1
1 3 7 4 2 1
1 3 1 2 4 7
class Solution {
public:
void nextPermutation(vector<int> &num) {
int i, j, n = num.size();
for (i = n - 2; i >= 0; --i) {
if (num[i + 1] > num[i]) {
for (j = n - 1; j > i; --j) {
if (num[j] > num[i]) break;
}
swap(num[i], num[j]);
reverse(num.begin() + i + 1, num.end());
return;
}
}
reverse(num.begin(), num.end());
}
};
class Solution {
public:
void nextPermutation(vector<int>& nums) {int n = nums.size(), i = n - 2, j = n - 1;
while (i >= 0 && nums[i] >= nums[i + 1]) --i;
if (i >= 0) {
while (nums[j] <= nums[i]) --j;
swap(nums[i], nums[j]);
}
reverse(nums.begin() + i + 1, nums.end());
}
};
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
建立一个保存最终结果的大集合res,还要定义一个保存每一个组合的小集合out,每次放一个数到out里,
如果out里数个数到了k个,则把out保存到最终结果中,否则在下一层中继续调用递归。
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> res;
vector<int> out;
helper(n, k, 1, out, res);
return res;
}
void helper(int n, int k, int level, vector<int>& out, vector<vector<int>>& res) {
if (out.size() == k) {res.push_back(out); return;}
for (int i = level; i <= n; ++i) {
out.push_back(i);
helper(n, k, i + 1, out, res);
out.pop_back();
}
}
};
C(n, k) = C(n-1, k-1) + C(n-1, k)
在n个数中取k个数的组合项个数,等于在n-1个数中取k-1个数的组合项个数再加上在n-1个数中取k个数的组合项个数之和。
C(4, 2) = C(3, 1) + C(3, 2)
我们不难写出 C(3, 1) 的所有情况:[1], [2], [3],还有 C(3, 2) 的所有情况:[1, 2], [1, 3], [2, 3]。
我们发现二者加起来为6,正好是 C(4, 2) 的个数之和。
但是我们仔细看会发现,C(3, 2)的所有情况包含在 C(4, 2) 之中,但是 C(3, 1) 的每种情况只有一个数字,而我们需要的结果k=2,其实很好办,每种情况后面都加上4,于是变成了:[1, 4], [2, 4], [3, 4],加上C(3, 2) 的所有情况:[1, 2], [1, 3], [2, 3],正好就得到了 n=4, k=2 的所有情况了。
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
if (k > n || k < 0) return {};
if (k == 0) return {{}};
vector<vector<int>> res = combine(n - 1, k - 1);
for (auto &a : res) a.push_back(n);
for (auto &a : combine(n - 1, k)) res.push_back(a);
return res;
}
};
每次先递增最右边的数字,存入结果res中,
当右边的数字超过了n,则增加其左边的数字,然后将当前数组赋值为左边的数字,再逐个递增,
直到最左边的数字也超过了n,停止循环。对于n=4, k=2时,遍历的顺序如下所示:
0 0 #initialization
1 0
1 1
1 2 #push_back
1 3 #push_back
1 4 #push_back
1 5
2 5
2 2
2 3 #push_back
2 4 #push_back
...
3 4 #push_back
3 5
4 5
4 4
4 5
5 5 #stop
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> res;
vector<int> out(k, 0);
int i = 0;
while (i >= 0) {
++out[i];
if (out[i] > n) --i;
else if (i == k - 1) res.push_back(out);
else {
++i;
out[i] = out[i - 1];
}
}
return res;
}
};
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
level
其本质是记录当前已经拼出的个数,一旦其达到了 nums
数组的长度,说明此时已经是一个全排列了,因为再加数字的话,就会超出
level
要从0开始遍历,因为这是求全排列,每个位置都可能放任意一个数字
数字有可能被重复使用,由于全排列是不能重复使用数字的,所以需要用一个 visited
数组来标记某个数字是否使用过
class Solution {
public:
vector<vector<int>> permute(vector<int>& num) {
vector<vector<int>> res;
vector<int> out, visited(num.size(), 0);
permuteDFS(num, 0, visited, out, res);
return res;
}
void permuteDFS(vector<int>& num, int level, vector<int>& visited, vector<int>& out, vector<vector<int>>& res) {
if (level == num.size()) {res.push_back(out); return;}
for (int i = 0; i < num.size(); ++i) {
if (visited[i] == 1) continue;
visited[i] = 1;
out.push_back(num[i]);
permuteDFS(num, level + 1, visited, out, res);
out.pop_back();
visited[i] = 0;
}
}
};
每次交换 num
里面的两个数字,经过递归可以生成所有的排列情况
class Solution {
public:
vector<vector<int>> permute(vector<int>& num) {
vector<vector<int>> res;
permuteDFS(num, 0, res);
return res;
}
void permuteDFS(vector<int>& num, int start, vector<vector<int>>& res) {
if (start >= num.size()) res.push_back(num);
for (int i = start; i < num.size(); ++i) {
swap(num[start], num[i]);
permuteDFS(num, start + 1, res);
swap(num[start], num[i]);
}
}
};
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
我们要避免重复的产生,在递归函数中要判断前面一个数和当前的数是否相等,
如果相等,且其对应的 visited 中的值为1,当前的数字才能使用,否则需要跳过,这样就不会产生重复排列了
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> res;
vector<int> out, visited(nums.size(), 0);
sort(nums.begin(), nums.end());
permuteUniqueDFS(nums, 0, visited, out, res);
return res;
}
void permuteUniqueDFS(vector<int>& nums, int level, vector<int>& visited, vector<int>& out, vector<vector<int>>& res) {
if (level >= nums.size()) {res.push_back(out); return;}
for (int i = 0; i < nums.size(); ++i) {
if (visited[i] == 1) continue;
if (i > 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0) continue;
visited[i] = 1;
out.push_back(nums[i]);
permuteUniqueDFS(nums, level + 1, visited, out, res);
out.pop_back();
visited[i] = 0;
}
}
};
用 TreeSet
来保存结果,利用其不会有重复项的特点,
然后在递归函数中 swap
的地方,判断如果i
和 start
不相同,
但是 nums[i]
和 nums[start]
相同的情况下跳过,继续下一个循环
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
set<vector<int>> res;
permute(nums, 0, res);
return vector<vector<int>> (res.begin(), res.end());
}
void permute(vector<int>& nums, int start, set<vector<int>>& res) {
if (start >= nums.size()) res.insert(nums);
for (int i = start; i < nums.size(); ++i) {
if (i != start && nums[i] == nums[start]) continue;
swap(nums[i], nums[start]);
permute(nums, start + 1, res);
swap(nums[i], nums[start]);
}
}
};
给出集合 [1,2,3,…,n]
,其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
说明:
示例 1:
输入: n = 3, k = 3
输出: "213"
示例 2:
输入: n = 4, k = 9
输出: "2314"
1234
1243
1324
1342
1423
1432
2134
2143
2314
2341
2413
2431
3124
3142
3214
3241
3412
3421
4123
4132
4213
4231
4312
4321
每一位上 1,2,3,4 分别都出现了6次,当最高位上的数字确定了,
第二高位每个数字都出现了2次,当第二高位也确定了,第三高位上的数字都只出现了1次,
当第三高位确定了,那么第四高位上的数字也只能出现一次
规律
class Solution {
public:
string getPermutation(int n, int k) {
string res;
string num = "123456789";
vector<int> f(n, 1);
for (int i = 1; i < n; ++i) f[i] = f[i - 1] * i;
--k;
for (int i = n; i >= 1; --i) {
int j = k / f[i - 1];
k %= f[i - 1];
res.push_back(num[j]);
num.erase(j, 1);
}
return res;
}
};
题目描述
给定一个字符串,判断该字符串中是否可以通过重新排列组合,形成一个回文字符串。
示例 1:
输入: "code"
输出: false
1
2
示例 2:
输入: "aab"
输出: true
1
2
示例 3:
输入: "carerac"
输出: true
HashMap
分字符串的个数是奇偶的情况来讨论,
如果是偶数的话,由于回文字符串的特性,每个字母出现的次数一定是偶数次,
当字符串是奇数长度时,只有一个字母出现的次数是奇数,其余均为偶数,
那么利用这个特性我们就可以解题,我们建立每个字母和其出现次数的映射,
然后我们遍历 HashMap
,统计出现次数为奇数的字母的个数,
那么只有两种情况是回文数,
第一种是没有出现次数为奇数的字母,再一个就是字符串长度为奇数,且只有一个出现次数为奇数的字母
class Solution {
public:
bool canPermutePalindrome(string s) {
unordered_map<char, int> m;
int cnt = 0;
for (auto a : s) ++m[a];
for (auto a : m) {
if (a.second % 2 == 1) ++cnt;
}
return cnt == 0 || (s.size() % 2 == 1 && cnt == 1);
}
};
HashSet
我们遍历字符串,如果某个字母不在 HashSet
中,我们加入这个字母,如果字母已经存在,我们删除该字母,
那么最终如果 HashSet
中没有字母或是只有一个字母时,说明是回文串
class Solution {
public:
bool canPermutePalindrome(string s) {
unordered_set<char> st;
for (auto a : s) {
if (!st.count(a)) st.insert(a);
else st.erase(a);
}
return st.empty() || st.size() == 1;
}
};
给定一个字符串 s ,返回其通过重新排列组合后所有可能的回文字符串,并去除重复的组合。
如不能形成任何回文排列时,则返回一个空列表。
示例 1:
输入: "aabb"
输出: ["abba", "baab"]
示例 2:
输入: "abc"
输出: []
由于回文字符串有奇偶两种情况,
偶数回文串例如 abba
,可以平均分成前后半段,
而奇数回文串例如 abcba
,需要分成前中后三段,需要注意的是中间部分只能是一个字符,
可以分析得出,如果一个字符串的回文字符串要存在,那么奇数个的字符只能有0个或1个,其余的必须是偶数个,
所以可以用哈希表来记录所有字符的出现个数,然后找出出现奇数次数的字符加入 mid
中,
如果有两个或两个以上的奇数个数的字符,则返回空集,对于每个字符,不管其奇偶,都将其个数除以2的个数的字符加入t中,这样做的原因是如果是偶数个,将其一般加入t中,
如果是奇数,如果有1个,除以2是0,不会有字符加入t,如果是3个,除以2是1,取一个加入t。
等获得了t之后,t是就是前半段字符,对其做全排列,每得到一个全排列,加上 mid
和该全排列的逆序列就是一种所求的回文字符串,这样就可以得到所有的回文全排列了。
在全排序的子函数中有一点需要注意的是,如果直接用数组来保存结果时,并且t中如果有重复字符的话可能会出现重复项,比如 t = "baa"
的话,那么最终生成的结果会有重复项,不信可以自己尝试一下。
这里简单的说明一下,当 start=0,i=1
时,交换后得到 aba,在之后当 start=1,i=2
时,交换后可以得到 aab
。
但是在之后回到第一层当baa后,当 start=0,i=2
时,交换后又得到了 aab
,重复就产生了。
那么其实最简单当去重复的方法就是将结果 res 定义成 HashSet
,利用其去重复的特性,可以保证得到的是没有重复的
class Solution {
public:
vector<string> generatePalindromes(string s) {
unordered_set<string> res;
unordered_map<char, int> m;
string t = "", mid = "";
for (auto a : s) ++m[a];
for (auto it : m) {
if (it.second % 2 == 1) mid += it.first;
t += string(it.second / 2, it.first);
if (mid.size() > 1) return {};
}
permute(t, 0, mid, res);
return vector<string>(res.begin(), res.end());
}
void permute(string &t, int start, string mid, unordered_set<string> &res) {
if (start >= t.size()) {
res.insert(t + mid + string(t.rbegin(), t.rend()));
}
for (int i = start; i < t.size(); ++i) {
if (i != start && t[i] == t[start]) continue;
swap(t[i], t[start]);
permute(t, start + 1, mid, res);
swap(t[i], t[start]);
}
}
};
给定一个按照升序排列的整数数组 nums
,和一个目标值 target
。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]
。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
首先对原数组使用二分查找法,找出其中一个目标值的位置,然后向两边搜索找出起始和结束的位置
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int idx = search(nums, 0, nums.size() - 1, target);
if (idx == -1) return {-1, -1};
int left = idx, right = idx;
while (left > 0 && nums[left - 1] == nums[idx]) --left;
while (right < nums.size() - 1 && nums[right + 1] == nums[idx]) ++right;
return {left, right};
}
int search(vector<int>& nums, int left, int right, int target) {
if (left > right) return -1;
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] < target) return search(nums, mid + 1, right, target);
else return search(nums, left, mid - 1, target);
}
};
使用两次二分查找法,第一次找到左边界,第二次调用找到右边界即可
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res(2, -1);
int left = 0, right = nums.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) left = mid + 1;
else right = mid;
}
if (right == nums.size() || nums[right] != target) return res;
res[0] = right;
right = nums.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) left = mid + 1;
else right = mid;
}
res[1] = right - 1;
return res;
}
};
只使用一个二分查找的子函数,来同时查找出第一个和最后一个位置。
如何只用查找第一个大于等于目标值的二分函数来查找整个范围呢,这里用到了一个小 trick,
首先来查找起始位置的 target
,就是在数组中查找第一个大于等于 target
的位置,当返回的位置越界,或者该位置上的值不等于 target
时,表示数组中没有 target
,直接返回 {-1, -1} 即可。
若查找到了 target
值,则再查找第一个大于等于 target+1
的位置,然后把返回的位置减1,就是 target
的最后一个位置,即便是返回的值越界了,减1后也不会越界
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int start = firstGreaterEqual(nums, target);
if (start == nums.size() || nums[start] != target) return {-1, -1};
return {start, firstGreaterEqual(nums, target + 1) - 1};
}
int firstGreaterEqual(vector<int>& nums, int target) {
int left = 0, right = nums.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) left = mid + 1;
else right = mid;
}
return right;
}
};
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n
个版本 [1, 2, ..., n]
,你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version)
接口来判断版本号 version
是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例:
给定 n = 5,并且 version = 4 是第一个错误的版本。
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
由于这题很有规律,好版本和坏版本一定有个边界,那么我们用二分法来找这个边界,对mid值调用API函数,
如果是坏版本,说明边界在左边,则把mid赋值给right,
如果是好版本,则说明边界在右边,则把mid+1赋给left,最后返回left即可。
需要注意的是,OJ里有个坑,那就是如果left和right都特别大的话,那么left+right可能会溢出,我们的处理方法就是变成left + (right - left) / 2,很好的避免的溢出问题
bool isBadVersion(int version);
class Solution {
public:
int firstBadVersion(int n) {
int left = 1, right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (isBadVersion(mid)) right = mid;
else left = mid + 1;
}
return left;
}
};
标签:++i 表示 mat ued ble 长度范围 长度 结果 它的
原文地址:https://www.cnblogs.com/wwj99/p/13042857.html