《数据结构》(408代码题)
2009 单链表(双指针)
分析:首先呢,给我们的数据结构是一个带有表头结点的单链表,也不允许我们改变链表的结构。链表的长度不是直接给出的啊,所以这个倒数也很棘手。那我们该如何解决这个“k”呢,我们并不能一下子就知道这个倒数第k位置在哪里,不过不妨倒着想一下,如果现在有一个指针指向尾结点,又有一个指针指向倒数第k个。那我们再逆推一下过程这两个指针一起往回走,当先前指向倒数第k个结点的指针走到表头与list相会时,后面那个指针是不是也到正数第k个结点的头上了?那是不是我们的问题就解决了。
思路:设置两个指针p、q,初始时都指向list,让q先往后走k步(计数器实现),这时再让p、q同时朝后走,直至q到达尾指针(所指的next为空),那么此时此刻的p所指向的结点既是我们所需要的倒数第k个结点,将其data值输出。
详细实现步骤:
- 初始化指针: 设置两个指针
p
和q
,初始时都指向链表的头节点list
。 - 移动快指针: 让指针
q
向前移动k步。这里需要注意的是,如果k
大于链表的长度,那么查找失败,因为不存在倒数第k个节点。 - 同步移动双指针: 当
q
成功移动了k步并且q
不为空(即没有到达链表末尾)时,开始同步移动p
和q
两个指针。每次循环,p
和q
都同时向后移动一步,即p = p->next;
和q = q->next;
。 - 查找结束条件: 当指针
q
到达链表的末尾(即q->next
为NULL
)时,停止移动。此时,p
所指向的节点就是链表中倒数第k个节点。 - 检查并输出结果: 检查指针
p
是否为NULL
。如果p
不为空,说明找到了倒数第k个节点,输出该节点的data
域的值,并返回1表示成功。如果p
为空,说明没有找到倒数第k个节点,返回0表示失败。 - 返回结果: 函数返回查找的结果,即1或0。
注:有可能参考答案里头并不是这样写的呀,我还没搞懂这个评分细则。(大概是这样)
// 假设LinkList是如下定义的结构体
typedef struct LinkNode {int data;struct LinkNode *next;
} LinkList;int findKthFromEnd(LinkList list, int k) {LinkList p, q;p = list;q = list;int cnt = 0;// 让q先向后走k步while (cnt < k && q != NULL) {q = q->next;cnt++;}// 当q到达第k+1个节点或链表尾部时,p和q一起向后移动while (q != NULL) {p = p->next;q = q->next;}// 此时p指向的就是倒数第k个节点,如果存在的话if (p != NULL) {printf("%d", p->data);return 1; // 查找成功} else {return 0; // 查找失败}
}
2010 顺序表
分析:
“循环”左移,那这个循环就应该是我们需要重点思考的点。先考虑最简单的我们可以设置两个数组,其中一个数组保存的是原数据,另一个初始为空。接着想要实现循环左移就只需要找出相对应的位置再一一赋值即可。(我比较笨只会右移,就把左移模拟成右移了)
到这会儿又可以开始想一下如何在时间和空间上优化了。空间的话,我们是可以只用一个数组(+一个中间变量+循环遍历)来实现的。原本我觉得已经可以了啊,然后看了书上觉得比较秒也更好实现。在时间上呢,也是能够发现一个规律。
1、算法思想
- 反转数组:首先反转整个数组 R。
- 反转前 n−p 个元素:接着反转数组的前 n−p 个元素。
- 反转后 p 个元素:最后反转数组的后 p 个元素。
2、算法实现
// 反转数组的辅助函数
void reverse(int arr[], int start, int end) {while (start < end) {// 交换元素int temp = arr[start];arr[start] = arr[end];arr[end] = temp;start++;end--;}
}// 循环左移 p 个位置
void rotate(int arr[], int n, int p) {// 反转整个数组reverse(arr, 0, n - 1);// 反转前 n - p 个元素reverse(arr, 0, n - p - 1);// 反转后 p 个元素reverse(arr, n - p, n - 1);
}
3、复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(1)
- 常数空间使用:该算法仅使用了几个额外的变量(如
temp
、start
和end
),这些变量的数量与输入数组的大小无关。 - 不使用额外数组:算法在进行反转时,直接在原数组上进行操作,而不需要创建新的数组来存放中间结果。
- 常数空间使用:该算法仅使用了几个额外的变量(如
2011 顺序表
分析:
先不考虑细节问题,最简单的做法当然是先将两个数组合并啦然后直接找中位数。
但是我们仔细想想我们只需要找出来并不需要实际的再花额外空间去合并,故只需设置一个计数器和两个指针从两个数组头开始遍历比大小,小的放行,大的留着下一趟比,然后再将计数器加1,加到一个数组的长度时即是我们所求的答案。那这样的话,时间复杂度就是O(n),空间复杂度就是O(1) 。
int findMedian(int A[], int B[], int n) { //n是一个升序序列的长度int i = 0, j = 0;int cnt = 0;int temp = 0;while (cnt < n) {if (i < n && (j >= n || A[i] <= B[j])) {temp = A[i++];} else {temp = B[j++];}cnt++;}return temp;
}
书里头给了这种就是二分咯,没有往这个方面想,那时间复杂度就会缩减到O(log2n),空间复杂度就还是一样的。
#include <stdio.h>int findMedian(int A[], int B[], int n) {int s1 = 0, d1 = n - 1;int s2 = 0, d2 = n - 1;while (s1 < d1 || s2 < d2) {int ml = (s1 + d1) / 2;int m2 = (s2 + d2) / 2;if (A[ml] == B[m2]) {return A[ml]; // 找到中位数}if (A[ml] < B[m2]) {if ((d1 - s1 + 1) % 2 == 1) { // 奇数个元素s1 = ml; // 保留中间点} else { // 偶数个元素s1 = ml + 1; // 舍弃前半部分}d2 = m2; // 舍弃 B 的后半部分} else {if ((d2 - s2 + 1) % 2 == 1) { // 奇数个元素d1 = ml; // 舍弃 A 的后半部分} else { // 偶数个元素d1 = ml - 1; // 保留中间点}s2 = m2 + 1; // 舍弃 B 的前半部分}}// 返回中位数if (s1 > d1) return B[s2]; // A 完全被舍弃if (s2 > d2) return A[s1]; // B 完全被舍弃return (A[s1] < B[s2]) ? A[s1] : B[s2]; // 返回较小的
}// 测试函数
int main() {int A[] = {1, 3, 8, 9, 15};int B[] = {7, 11, 18, 19, 21};int n = sizeof(A) / sizeof(A[0]);int median = findMedian(A, B, n);printf("中位数是: %d\n", median);return 0;
}
2012 单链表(双指针)
分析:最简单的应该是,直接安排两个指针遍历两个链表,再将其数据域的值赋给两个辅助数组,然后根据数组可以比较简单的先把共同后缀比出来,再利用两个index值来判断除去共同后缀之后的长度为多少,再让指针遍历一遍链表,到相对应的位置之后让其中一个指向另一个所指向的结点。(不考虑释放另一边所占用的空间)
(书里那个方法我想不到啊 q"o"q)
算法的基本设计思想:
- 确定较长链表: 首先确定两个链表中较长的一个,因为共同后缀不可能长于较短的链表。
- 移动较短链表的指针: 将较短链表的指针移动到与较长链表相同长度的位置。
- 同时遍历: 从这个点开始,同时遍历两个链表,比较每个节点的值。
- 找到共同后缀: 当两个指针同时到达链表末尾或者发现不匹配的节点时,停止遍历。如果到达末尾,说明找到了共同后缀;否则,记录下不匹配时两个链表的指针位置。
算法的详细实现步骤:
- 计算两个链表的长度。
- 使用较长链表的长度减去较短链表的长度,得到需要前进的步数。
- 将较长链表的头指针移动到倒数第k个节点,其中k为需要前进的步数。
- 同时遍历两个链表,直到两个指针都到达末尾或者发现不匹配的节点。
- 如果两个指针都到达末尾,返回较长链表的当前节点,即为共同后缀的起始位置。
- 如果发现不匹配的节点,返回
nullptr
,表示没有共同后缀。
// 定义链表节点结构
struct ListNode {int data;ListNode* next;ListNode(int val) : data(val), next(nullptr) {}
};// 计算链表长度
int getLength(ListNode* head) {int length = 0;ListNode* temp = head;while (temp != nullptr) {length++;temp = temp->next;}return length;
}// 找到共同后缀的起始位置
ListNode* findCommonSuffix(ListNode* str1, ListNode* str2) {int len1 = getLength(str1);int len2 = getLength(str2);// 确定较长的链表并移动指针ListNode* longer = (len1 > len2) ? str1 : str2;ListNode* shorter = (len1 > len2) ? str2 : str1;// 计算步数并移动较长链表的指针int steps = std::abs(len1 - len2);for (int i = 0; i < steps; ++i) {if (longer == nullptr) return nullptr; // 防止链表为空longer = longer->next;}// 同时遍历两个链表while (longer != nullptr && shorter != nullptr) {if (longer->data != shorter->data) {return nullptr; // 如果发现不匹配的节点,返回nullptr}longer = longer->next;shorter = shorter->next;}// 如果遍历结束于链表末尾,返回共同后缀的起始位置if (longer == nullptr && shorter == nullptr) {return nullptr; // 两个链表都到达末尾,表示没有共同后缀}// 如果 shorter 先到达末尾,返回 longer 的当前节点return longer; // 此时 longer 指向共同后缀的起始位置
}
2013 顺序表
分析:
最简单的做法应该可能大概是直接rua个大数组(n范围没讲,就只能尽量大咯)然后对这个整数数列进行扫描,将其值映射到大数组中的下标,然后大数组该对应下标的元素值加1(也就是遍历一遍整数数列,用大数组统计这个整数数列的每个数都出现了多少次)最后如果大数组中元素最大的值未能大于长度的一般则输出-1。
int MajorityElement(int A[], int n) {// 创建大小为 n 的统计数组并初始化为 0int *count = (int *)calloc(n, sizeof(int));// 遍历整数序列,统计每个数的出现次数for (int i = 0; i < n; i++) {count[A[i]]++;}// 查找出现次数最多的元素int majorityElement = -1;int maxCount = 0;for (int i = 0; i < n; i++) {if (count[i] > maxCount) {maxCount = count[i];majorityElement = i;}}// 检查是否为主元素if (maxCount > n / 2) {free(count); // 释放内存return majorityElement;} else {free(count); // 释放内存return -1; // 不存在主元素}
}
书里这个应该是一种特殊的算法蛤,没必要背,了解即可。
int fMajorityElement(int A[], int n) {int candidate = -1; // 候选元素int count = 0; // 计数器// 第一步:选取候选元素for (int i = 0; i < n; i++) {if (count == 0) {candidate = A[i]; // 更新候选元素count = 1; // 重置计数器} else if (A[i] == candidate) {count++; // 候选元素的支持度加一} else {count--; // 其他元素的支持度减一}}// 第二步:验证候选元素count = 0; // 重置计数器for (int i = 0; i < n; i++) {if (A[i] == candidate) {count++; // 统计候选元素出现的次数}}// 检查候选元素是否为主元素if (count > n / 2) {return candidate; // 返回主元素} else {return -1; // 不存在主元素}
}
原理来自 Boyer-Moore 投票算法,它是一种高效的寻找主元素的算法。
-
候选元素的选取:
- 使用一个计数器来记录当前候选元素的“支持度”。
- 遍历数组时,如果计数器为零,选择当前元素作为新的候选元素,并将计数器设为1。
- 如果当前元素与候选元素相同,增加计数器;如果不同,减少计数器。
-
为什么有效:
- 如果存在一个元素的出现次数超过 n/2,那么在遍历过程中,该元素将始终保持为候选元素。
- 计数器的增加和减少保证了在元素数量相等时,候选元素的优势可以保持。
具体步骤:
-
初始化:
- 设置一个候选元素
candidate
和一个计数器count
,初始值为 0。
- 设置一个候选元素
-
遍历数组:
- 对于每个元素:
- 如果
count
为 0,更新candidate
为当前元素,并将count
设置为 1。 - 如果当前元素等于
candidate
,则count
加 1。 - 如果当前元素不等于
candidate
,则count
减 1。
- 如果
- 对于每个元素:
-
验证候选元素:
- 遍历数组,统计
candidate
的出现次数,确认是否超过 n/2。
- 遍历数组,统计
2014 二叉树(WPL)
1. 算法基本设计思想
带权路径长度(WPL)是二叉树中所有叶结点的权值与其到根节点的路径长度的乘积之和。为了计算 WPL,可以采用深度优先遍历(DFS)的方法,遍历树的每一个节点:
- 对于每个节点,记录当前深度。
- 当到达叶节点时,计算其带权路径长度 WPL+=weight×depth WPL+=weight×depth
2. 二叉树节点的数据类型定义
typedef struct BiNode {struct BiNode* left; // 指向左子树的指针struct BiNode* right; // 指向右子树的指针int weight; // 节点的权值(仅在叶节点有效)
};
3. WPL 计算的 C++ 实现
以下是计算二叉树 WPL 的算法实现:
//Way1:先序遍历
int ans=0;
void func(BiTree root,int depth){if(root==NULL) return;if(root->left==NULL&&root->right==NULL){ans+=root->weight*depth;}func(root->left,depth+1);func(root->right,depth+1);
}int WPL(Bitree root){func(root,0);return ans;
}
- 时间复杂度:O(n),其中 nn 是树中节点的数量,因为每个节点都被访问一次。
- 空间复杂度:O(h),其中 hh 是树的高度,递归调用栈的深度。
这样就完成了 WPL 的计算算法设计与实现!
//Way2:层次遍历
int func(BiTree root, int depth) {queue<BiTNode*> q; // 创建队列用于层次遍历int ans = 0, depth = 0; // 初始化结果ans和当前深度depthif (root != NULL) q.push(root); // 如果根节点不为空,则入队while (!q.empty()) { // 当队列不为空时循环int n = q.size(); // 获取当前队列的大小,即当前层的节点数for (int i = 0; i < n; i++) { // 遍历当前层的所有节点BiTNode* node = q.front(); // 获取队列前端的节点q.pop(); // 弹出队列前端的节点if (node->left != NULL) q.push(node->left); // 如果左孩子不为空,则入队if (node->right != NULL) q.push(node->right); // 如果右孩子不为空,则入队// 如果当前节点为叶子节点(没有左右孩子)if (node->left == NULL && node->right == NULL) {ans += node->weight * depth; // 累加叶子节点的权重乘以当前深度}}depth++; // 每遍历完一层,深度增加}return ans; // 返回最终计算的结果
}
2015 单链表(双指针)
分析:首先,依然还是单链表所以其上的指针只能一条路走到黑,那我们其实可以设置两个指针一个走慢一点,走在快指针的后一步。那这样是不是就可以实现找前驱的工作了。然后我们可以再设置一个遍历指针,在快慢指针行进过程中的每一步都从慢指针开始向后遍历。在找到绝对值相同的结点时,利用快慢指针实现删除操作(快指针走一步,慢指针直接指向快指针)。这样实现起来很简单对吧,但是时间复杂度有点点高,需要O(n^2)
不对不对是保留第一次出现的,那就固定一个指针用于遍历,该指针每遍历一个结点就发出一对快慢指针用于删除后续绝对值重复的结点。但是时间复杂度也依旧是O(n^2)。
那如果我先遍历一遍单链表将其数据域的值都用一个cnt记录出现次数(初始为-1,出现1次自动加1)然后最后再用两个指针进行遍历,遍历的同时比对所存放的cnt值是否大于0(重复程度)。所有数据第一次遇到均不会删除只会cnt-1,若后续重复遇到即可根据cnt的值来判断之后要删除几个结点(也同样是用快慢指针来实现删除结点的操作),这时候时间复杂度应该为O(n^2)。
不对不对,我们可以一趟就完成这个事情,直接边遍历边记录。只需要利用数组在遍历的同时记录下链表中已经出现的数值,后续都先过一遍数组(但是又因为数组是可以随机存储的所以不影响)看看该值是否出现过,再碰到直接一删除不就完了。
(书里给的也是这样蛤,但是是直接申请空间来表示)
注:真是优雅
2016 快速排序
分析:它既要n1-n2的绝对值尽可能小,又要S1-S2的绝对值尽可能大,那不就是最好分成两份差不多均等的,大的放右边,小的放左边嘛,那这是啥?不就是快排嘛
算法思路:
- 初始化变量,
low
和high
用于标记数组的边界,low0
和high0
用于在划分过程中保存边界,k
为要找的第k小的元素的位置,s1
和s2
分别用于存储前k小和后n-k小的元素之和。 - 使用while循环进行快速选择算法的划分过程,直到找到第k小的元素。
- 在每次迭代中,选择
low
位置的元素作为枢轴(pivot),然后移动low
和high
指针来划分数组,使得左边的元素都不大于枢轴,右边的元素都不小于枢轴。 - 如果枢轴元素恰好位于第k小的位置,结束划分;否则,根据枢轴的位置调整
low0
和high0
,然后继续划分。 - 找到第k小的元素后,遍历数组,计算前k小的元素之和与后n-k小的元素之和,返回它们的差值。
int setPartition(int a[], int n) {int pivotkey, low = 0, low0 = 0, high = n - 1, high0 = n - 1, flag = 1, k = n / 2, i;int s1 = 0, s2 = 0;while (flag) {pivotkey = a[low];// 选择枢轴while (low < high && a[high] >= pivotkey) --high;if (low != high) a[low] = a[high];while (low < high && a[low] <= pivotkey) ++low;if (low != high) a[high] = a[low];// 基于枢轴对数据进行划分if (low == k - 1) {// 如果枢轴是第n/2小元素,划分成功,flag=0;flag = 0;} else {// 是否继续划分if (low < k - 1) {low0 = ++low;high = high0;} else {high0 = --high;low = low0;}}}// 计算前k小的元素之和与后n-k小的元素之和for (i = 0; i < k; i++) s1 += a[i];for (i = k; i < n; i++) s2 += a[i];return s2 - s1;
}
时间复杂度:O(n)
空间复杂度:O(1)
2017 二叉树(InOrder)
分析:要实现将给定的二叉树转换成为等价的中缀表达式并输出,那其实我们只需要中序遍历就好了,一边遍历一边输出,最后的结果自然就是该题想要我们实现的。任务就转换成为对中序遍历稍加改变一下咯。
还有还有为了保证输出出来的中缀表达式有正确的运算顺序,我们还应该在合适的位置添加括号。(遍历左子树之前加上,遍历完右子树之后也加上)
typedef struct node{char data[]; struct node *left, *right;
}BTree;//BreeToTree(root,1); //调用该程序 void BreeToTree(BTree *root,int deep){if(root!=NULL){if(root->left=NULL&&root->right=NULL){printf("%s",root->data); }else{if(deep>1) printf("("); //遍历左子树之前 BreeToTree(root->left,deep+1); //递归遍历左子树printf("%s",root->data);BreeToTree(root->right,deep+1); //递归遍历右子树if(deep>1) printf(")"); //遍历右子树之后 } }else{return; //空结点直接返回 }
}
2018 顺序表
分析:
先想想最容易实现的是什么呢?自然也还是可以设置一个辅助数组专门记录其下标是否出现过,最后在从小到大第一个未被标记过的不就是我们想找的最小正整数吗。
然后我们再看回要求,这题只要求时间上高效喔,那就是极致的空间换时间,快就完事了呗。上述思路的时间复杂度为O(n)嘞,那我们思考下有没有什么是可以更快做出来的呢?是不是也可以直接先过一遍排序(但在这个过程中遇到负数就直接跳过)然后遍历再找一遍是不是也出结果了(不过要是想稳定的话,剩下的几个排序算法的时间复杂度也为O(n))。
注:
书里所给出的和我第一次的想法一样蛤。
int findMissMin(int A[], int n) {int i;int *B; // 标记数组// 分配空间,使用malloc而不是错误的malocB = (int *)malloc(sizeof(int) * (n + 1)); // 需要为1到n的整数分配空间// 赋初值为0,注意memset的第二个参数应该是字节数memset(B, 0, sizeof(int) * (n + 1));// 遍历数组A,如果A[i]在1到n之间,则标记B[A[i] - 1]为1for (i = 0; i < n; i++) {if (A[i] > 0 && A[i] <= n) {B[A[i]] = 1; // 直接使用A[i]作为索引,因为我们要标记1到n的整数}}// 扫描数组B,找到第一个值为0的位置,即未出现的最小正整数for (i = 1; i <= n; i++) { // 从1开始,因为0不是正整数if (B[i] == 0) {break;}}// 返回结果,i是数组B中第一个未标记的位置,即未出现的最小正整数return i;
}
int findMissin(std::vector<int>& nums) {int n = nums.size();// 将每个正整数放到对应的索引位置for (int i = 0; i < n; i++) {// 只处理在范围内的正整数while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {// 交换到正确的位置std::swap(nums[i], nums[nums[i] - 1]);}}// 查找第一个缺失的正整数for (int i = 0; i < n; i++) {if (nums[i] != i + 1) {return i + 1; // 返回缺失的最小正整数}}return n + 1; // 如果所有位置都正确,返回n+1
}
注:第二种的空间复杂度会较低,但是题目也并未要求。
2019 单链表(双指针)
【2019统考真题】设线性表L=(a1,a2,a3,...,an-1,an-1,an)采用带头结点的单链表保存,链
表中的结点定义如下:typedef struct node { int data;struct node* next; }NODE
请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表L=(a1,an,a2,an-1,a3,an-2,...)要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释,
3)说明你所设计的算法的时间复杂度。
分析:这题有硬性要求咯,空间复杂度要为O(1)才行。那其实我们可以借助之前做的10年真题的想法,你看目的是不是要从头和从屁股开始交替形成一个新的链表,那其实我们可以先找到中间(向下取整的那个结点)然后反转后半段,再交替将其插入头结点之后的链表就完事了。
void changeList(NODE* h) {NODE *p, *q, *r, *s;p = q = h; // 初始化慢指针和快指针// 寻找中间节点while (q->next != NULL) {p = p->next; // 慢指针走一步q = q->next; // 快指针走一步if (q->next != NULL) {q = q->next; // 快指针再走一步}}// p所指节点为中间节点,q为后半段链表的首节点NODE* secondHalf = p->next; // 保存后半段链表p->next = NULL; // 切断前半段链表// 将链表后半段逆置NODE* prev = NULL;while (secondHalf != NULL) {r = secondHalf->next; // 保存下一个节点secondHalf->next = prev; // 反转指针prev = secondHalf; // 移动前驱secondHalf = r; // 移动到下一个节点}// prev 现在指向反转后的后半段链表的头s = h->next; // s指向前半段的第一个数据节点q = prev; // q指向后半段的第一个数据节点// 将链表后半段的节点插入到指定位置while (q != NULL) {r = q->next; // r指向后半段的下一个节点q->next = s->next; // 将q插入到s之后s->next = q; // 更新s的next指向qs = q->next; // s指向前半段的下一个插入点q = r; // 移动到下一个节点}
}
时间复杂度:O(n)
该算法需要遍历链表三次:一次找到中间节点,一次反转后半部分,一次合并两个链表。
2020 最短路径(Floyd)or 双指针
【2020统考真题】定义三元组(a,b,c)(abc均为整数)的距离D=|a-b|+b-c|+|c-a|。给定3个非空整数集合S1、S2和S3,按升序分别存储在3个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a,b,c)(a∈S1,b∈S2,c∈S3)中的最小距离。例如S1={-1,0,9},S2={-25,-10,10,11},S3={2,9,17,30,41},则最小距离为2,相应的三元组(9,10,9)。
要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C语言或C++语言描述算法,关键之处给出注释。
3)说明你所设计算法的时间复杂度和空间复杂度。
分析:既然一没要求时间二没要求空间,那我们直接上暴力!!!
第一层循环遍历S1,然后第二层是S2,第三层是S3(由外向内)然后再来个tempmin,暴力枚举出所有的可能性,再随着循环遍历依次比较当前最小,直至结束选出最优解。
// 计算三个数组S1、S2和S3中元素之间的最小距离
int min_dist(int S1[], int S2[], int S3[]) {// 用于存储产生最小距离的路径int path[3];// 初始化路径数组memset(path, 0, sizeof(path));// 计算数组S1的长度int l1 = sizeof(S1) / sizeof(*S1);// 计算数组S2的长度int l2 = sizeof(S2) / sizeof(*S2);// 计算数组S3的长度int l3 = sizeof(S3) / sizeof(*S3);// 初始化最小距离为一个很大的数int md = 0x3FFF; // 使用十六进制表示一个很大的数// 遍历数组S1for (int i = 0; i < l1; i++) {// 遍历数组S2for (int j = 0; j < l2; j++) {// 遍历数组S3for (int k = 0; k < l3; k++) {// 计算当前路径的总距离int td = std::abs(S1[i] - S2[j]) + std::abs(S2[j] - S3[k]) + std::abs(S3[k] - S1[i]);// 如果当前路径的总距离小于已知的最小距离,则更新最小距离和路径if (td < md) {md = td;path[0] = S1[i];path[1] = S2[j];path[2] = S3[k];}}}}// 输出产生最小距离的路径for (int i : path) {std::cout << i << std::endl;}// 返回最小距离return md;
}
更优解:暴力解法在集合大小较大时效率较低。一个可能的优化方法是使用双指针技巧,首先固定一个集合中的元素,然后在另外两个集合中使用双指针来寻找最小距离。这种方法可以减少一些不必要的计算,但仍然需要O(n1 * n2 + n2 * n3 + n3 * n1)的时间复杂度。如果S1、S2和S3中的元素都是有序的,那么双指针方法可以更有效地找到最小距离。(书里那个例图其实画的很清楚蛤)
int findMinDistance(const std::vector<int>& S1, const std::vector<int>& S2, const std::vector<int>& S3) {int minDist = std::numeric_limits<int>::max(); // 初始化最小距离为最大值int n1 = S1.size(), n2 = S2.size(), n3 = S3.size();// 遍历S1中的每个元素for (int a : S1) {int i = 0, j = 0; // 初始化指针// 双指针遍历S2和S3while (i < n2 && j < n3) {int b = S2[i];int c = S3[j];// 计算当前三元组的距离int dist = std::abs(a - b) + std::abs(b - c) + std::abs(c - a);minDist = std::min(minDist, dist); // 更新最小距离// 移动指针,寻找更优解if (b < c) {i++; // b较小,向右移动S2的指针} else {j++; // c较小或相等,向右移动S3的指针}}}return minDist;
}
注:该算法的思维还是很重要的,很多场合都可以用得到。
2021 图(邻接矩阵)
1)算法的基本设计思想
本算法题属于送分题,题干已经告诉我们算法的思想。对于采用邻接矩阵存储的无向图,在邻接矩阵的每一行(列)中,非零元素的个数为本行(列)对应顶点的度。可以依次计算连通图G中各顶点的度,并记录度为奇数的顶点个数,若个数为0或2,则返回1,否则返回0。
2)算法实现
int IsExistEL(MGraph G)(//采用邻接矩阵存储,判断图是否存在EL路径int degree, i, j, count=0;for(i=0;i<G. numVertices;i++){degree=0;for(int j=0;j<G.numVertices;j++){degree+=G.Edge[i][j]; //依次计算各个顶点的度 if(degree%2!=0) count++; //对度为奇数的顶点计数1}if(count==0||count==2) return 1; //存在EL路径,返回1else return 0; //不存在EL路径,返回0}
}
3)时间复杂度和空间复杂度
算法需要遍历整个邻接矩阵,所以时间复杂度是O(n^2),空间复杂度是O(1)。
2022 二叉树(BST)
【2022统考真题】已知非空二叉树T的结点值均为正整数,采用顺序存储方式保存,数据结构定义如下:
分析:都说是二叉排序树了嘛,那我就只需要在遍历过程中看一下是否有不满足定义(左边大于中间或者右边小于中间)的结点就好啦。那其实是不是也可以直接中序遍历完看序列是不是递增的就好啦。
// 检查数组是否表示一个二叉搜索树
int IsBST(int* SqBiTNode, int size) {int* pmax = (int*)malloc(size * sizeof(int));int* pmin = (int*)malloc(size * sizeof(int));if (!pmax || !pmin) {free(pmax);free(pmin);return 0;}// 初始化pmax和pmin数组for (int i = 0; i < size; i++) {int iLeft = 2 * i + 1;int iRight = 2 * i + 2;if (iLeft < size) pmax[i] = SqBiTNode[iLeft];if (iRight < size) pmin[i] = SqBiTNode[iRight];if (iLeft < size && SqBiTNode[i] <= pmax[iLeft]) return 0;if (iRight < size && SqBiTNode[i] >= pmin[iRight]) return 0;}free(pmax);free(pmin);return 1; // 所有节点都满足BST的条件
}
// 检查数组是否表示一个二叉搜索树
int IsBST(int* SqBiTNode, int size) {int val = INT_MIN; // 初始化为最小整数for (int i = 0; i < size; i++) {if (SqBiTNode[i] <= val) return 0; // 如果当前节点值小于等于val,则不是BSTval = SqBiTNode[i]; // 更新val为当前节点值}return 1; // 中序遍历得到的序列是升序的,因此是BST
}
2023 图(邻接矩阵)
(1)算法思想:
- 遍历有向图中所有顶点,并统计各顶点的出度和入度,输出出度大于入度的KJ页点,并使用变量 count 累计顶点的总数。
- 计算顶点i的出度: 遍历邻接矩阵的i行元素,即 Edge[i][0]~Edge[i][numVertex-1],统计非零元素个数,即为顶点i的出度
- 计算顶点i的入度:遍历邻接矩阵的i列元素,即Edge[0][i]~ Edge[numVertex-1][i],统计非零元素个数,即为顶点i的入度
(2)算法实现:
int printVertices (MGraph G){int count =0;//K顶点的总数for (int i=0; i<G.numVertex; i++){int outDegree = 0;//顶点i的出度int inDegree = 0;//顶点i的入度for (int j=0;j<G.numVertex; j++)if (G.Edge[i][j]>0) outDegree++;}for (int j=0;j<G.numVertex; j++)if (G.Edge[j][i]>0) outDegree++;//循环两次方便理解}if (outDegree > inDegree) [//顶点i的出度大于入度printf ("c\n",G.VertexList[i]);//输出顶点icount++;//累加K顶点总数}}return count;//返回x顶点总数
}
2024 图(邻接矩阵)and 拓扑排序
41.已知有向图G采用邻接矩阵存储,类型定义如下
typedef struct{ //图的类型定义int numVertices,numEdges; //图的顶点数和有向边数char VerticesList[MAXV]; //顶点表,MAXV为已定义常量int Edge[MAXV][MAXV]; //邻接矩阵 }MGraph;
请设计算法:int uniquely(MGraph G),判定G是否存在唯一 的拓扑序列,若是,则返回1,否则返回0,要求如下:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
分析:那咱首先先考虑一下怎么着才有唯一的拓扑排序呀。首先是不是找的都是入度为1的顶点,然后就是保证唯一的话是不是每一步找顶点的时候只能由一个满足条件(如果有多个就直接判断不唯一了)直到所有的顶点都被输出是不是就算是满足条件啦
基本思路:
- 创建一张入度表indegree[]用于记录各个顶点的入度情况
- 选择入度为0的点并将所有邻接结点的入度-1(借助邻接矩阵实现)
- 重复以上过程,若每次选中的入度为0的顶点有且仅有一个且全部遍历完,则存在唯一的拓扑排序;反之不存在或存在多个拓扑排序。
// 声明入度数组并分配内存
int *indegree;
indegree = (int *)malloc(G.numVertices * sizeof(int));// 初始化入度数组
for (int j = 0; j < G.numVertices; j++) {indegree[j] = 0;// 计算每个顶点的入度for (int i = 0; i < G.numVertices; i++) {indegree[j] += G.Edge[i][j];}
}// 拓扑排序相关变量
int count, zero_count, zero_vertex;
while (count < G.numVertices) {zero_count = 0;zero_vertex = -1;// 寻找入度为0的顶点for (int j = 0; j < G.numVertices; j++) {if (indegree[j] == 0) {zero_count++;zero_vertex = j;if (zero_count > 1) break; // 超过一个入度为0的点,退出}}// 如果有多个或没有入度0的节点if (zero_count != 1) {free(indegree);return 0; // 返回错误码}// 移除找到的入度为0的顶点,并更新相邻节点的入度count++;indegree[zero_vertex] = -1; // 标记为已处理for (int j = 0; j < G.numVertices; j++) {if (G.Edge[zero_vertex][j] > 0) {indegree[j]--;}}
}
相关文章:
《数据结构》(408代码题)
2009 单链表(双指针) 分析:首先呢,给我们的数据结构是一个带有表头结点的单链表,也不允许我们改变链表的结构。链表的长度不是直接给出的啊,所以这个倒数也很棘手。那我们该如何解决这个“k”呢,…...
哈希表的完善及unordered_map/unordered_set封装
1.哈希表的完善 1.1 优化:哈希函数 在实际使用中,往往需要以字符串作为存储依据(键值),比如姓名与快递信息、商品名称与价格、中文单词与英文释义等。 而在上一篇文章中,我们实现的哈希表只考虑了整型的存储情况,即直…...
“物联·数据·产融·场景”聚力垂直数智场景下的新质生产力破局
人工智能、物联网(简称AIOT)正在深刻改变世界的经济格局,对各行各业产生深厚的影响。12月6日,由深圳市物联网协会、华夏银行深圳分行、深圳市区块链技术应用协会、深圳市康复辅助器具智能技术应用协会联合主办的第五届AIOT生态大会…...
API接口的性能测试与优化策略
在现代软件开发中,API(应用程序编程接口)扮演着至关重要的角色,它们作为不同服务之间的桥梁,确保数据的顺畅流通与交互。然而,随着用户需求的不断增长和系统复杂性的提升,API接口的性能问题日益…...
【Vue】Part4 接口调用
接口调用方式 原生ajax基于jQuery的ajaxfetchaxios 异步 JavaScript的执行环境是「单线程」所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完后才能执行下一个,…...
管理系统前端框架开发案例学习
一、 需求分析 本案例的主要目标是开发一个智能学习辅助系统的前端界面,涵盖以下功能模块: 首页:显示系统的总体概览和关键功能介绍。 班级学员管理:实现班级管理和学员管理。 系统信息管理:管理部门和员工信息。 …...
协程设计原理与实现
协程设计原理与汇编实现 同步与异步 对于任何一个事情,都可以划分为不同的步骤。所谓同步,就先做第一个事情,按照这件事的步骤完成这件事之后,再去做第二件事。再去做第三件事,以此类推。 异步就是,可以…...
c++广播通讯的实现
概念大家都很清楚,不赘述。 广播必然用UDP这套东西。 setsockopt() 函数及其在广播中的应用: 在 C 网络编程中,setsockopt() 函数用于设置套接字选项,这些选项可以控制套接字的各种行为。对于广播通信,我们特别关心…...
Leetcode 3377. Digit Operations to Make Two Integers Equal
Leetcode 3377. Digit Operations to Make Two Integers Equal 1. 解题思路2. 代码实现 题目链接:3377. Digit Operations to Make Two Integers Equal 1. 解题思路 这一题的核心思路属于路径遍历问题,我们使用一个堆排来控制最优路径的选择。 我们首…...
高项 - 项目管理原则与项目绩效域
个人总结,仅供参考,欢迎加好友一起讨论 博文更新参考时间点:2024-12 高项 - 章节与知识点汇总:点击跳转 文章目录 高项 - 项目管理原则与项目绩效域项目管理12条原则原则1:成为勤勉、尊重和关心他人的管家 (p202)原则…...
【开源】A065—基于SpringBoot的库存管理系统的设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…...
LeetCode—189. 轮转数组(中等)
题目描述: 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 示例1: 输入: nums [1,2,3,4,5,6,7], k 3输出:[5,6,7,1,2,3,4] 解释: 向右轮转 1 步:[7,1,2,3,4,5,6] 向右轮转 2 步:[6,7,1,2,3,4,5] 向…...
fastadmin框架同时使用 阿里云oss和阿里云点播
背景 项目的实际需求中既要用到阿里云oss产品又用到阿里云点播系统,实现完美的统一。设置两个地址downUrl,thirdCode。分别代表阿里云oss上传路径和阿里云点播系统vId。 实现 默认框架你已经集成好阿里云oss集成工作,前端html页面实现 <…...
MySQL-DML之数据表操作
文章目录 一. 插入表记录1. 向表中插入部分字段2. 向表中插入所有字段,字段的顺序为创建表时的顺序3. 一次添加多条数据信息 二. 更新表记录1. 更新所有记录的指定字段 更新符号条件记录的指定字段2. 更新符号条件记录的指定字段 三. 删除表记录1. 按条件删除记录2. 清空记录 四…...
Android 逆向/反编译/Hook修改应用行为 基础实现
前言:本文通过一个简单的情景案例实现安卓逆向的基本操作 一、情景描述 本文通过一个简单的情景案例来实现安卓逆向的基本操作。在这个案例中所使用的项目程序是我自己的Demo程序,不会造成任何的财产侵害,本文仅作为日常记录及案例分享。实…...
【前端】理解 JavaScript 对象属性访问的复杂性
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 💯前言💯理论基础:JavaScript 对象属性的访问模式1. 点符号访问(Dot Notation)2. 方括号访问(Bracket Notation)点符号…...
进入 Dystopia:第九周游戏指南
本指南将为大家详细说明在第八周的每个体验中可以获得的奖励。 在杂草丛生的反乌托邦废墟中生存,随着大自然重新开垦这片土地,文明已陷入绝望。穿越高耸入云、摇摇欲坠的摩天大楼,抵御末世社会的各种危险。适应这个文明与荒野之间的界限已经消…...
Helm安装Mysql8主从复制集群
目录 一、Helm安装 二、安装mysql 1、拉取镜像 2、修改配置文件 3、创建mysql-secret 4、安装 一、Helm安装 这里不再赘叙,具体安装请参考官网 Helm | 快速入门指南 二、安装mysql 1、拉取镜像 #添加仓库 helm repo add bitnami https://charts.bitnami.c…...
[小白系列]GPU-nvidia-smi指令
nvidia-smi(NVIDIA System Management Interface)是一种命令行实用程序,用于监控和管理NVIDIA GPU(图形处理器)的状态和性能。它提供了一种简单而强大的方式来获取有关GPU的实时信息,并且可以用于诊断、…...
Flutter 图片编辑板(一) 事件路由
一个图片编辑板,有两部分组成。编辑板和内容项。每一个内容项是被InteractiveViewer修饰的widget,具有缩放偏移的功能。 在图片编辑板上, 会有多个内容相,图片或文字(添加文字目前还没做过)。 当要编辑其中…...
Cherno C++学习笔记 P33 字符串的字面量
在这篇文章当中我们介绍一下有关于字符串更加深入的知识,也就是字符串的字面量。 首先什么是字面量?其实也很简单,就是双引号里面的那一坨,其实就是字面量,我们举一个最简单的例子: #include<iostream…...
数据结构 (36)各种排序方法的综合比较
一、常见排序方法分类 插入排序类 直接插入排序:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。希尔排序:是插入排序的一种改进版本,先将整个待排序的记录序列分割成为…...
Master EDI 项目需求分析
Master Electronics 通过其全球分销网络,支持多种采购需求,确保能够为客户提供可靠的元件供应链解决方案,同时为快速高效的与全球伙伴建立合作,Master 选择通过EDI来实现与交易伙伴间的数据传输。 EDI为交易伙伴之间建立了一个安…...
java+ssm+mysql计算机等级考试网
项目介绍: 使用javassmmysql开发的计算机等级考试信息网,系统包含前后台,包含超级管理员,系统管理员角色,功能如下: 前台:首页;考试动态;相关资源下载;考试…...
MitelMiCollab 身份绕过导致任意文件读取漏洞复现(CVE-2024-41713)
0x01 产品描述: Mitel MiCollab 是一个企业协作平台,它将各种通信工具整合到一个应用程序中,提供语音和视频通话、消息传递、状态信息、音频会议、移动支持和团队协作功能。0x02 漏洞描述: Mitel MiCollab 的 NuPoint 统一消息 (NPM) 组件中存在身份验证绕过漏洞,由于输入…...
【Python]深入Python日志管理:从logging到分布式日志追踪的完整指南
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 日志是软件开发中的核心部分,尤其在分布式系统中,日志对于调试和问题定位至关重要。本篇文章将从Python标准库的logging模块出发,逐步探讨日志管理的最佳实践,涵盖日志配置、日志分层、日志格式化等基…...
python rstrip 的迷惑行为
在项目中,我需要把字符串末尾的一部分去掉,类似截断,我用ide的随笔提示,发现了rstrip这个方法,然后我试了下,满足我的需求,但在测试过程中,我发现了rstrip的一些行为很让我迷惑。 开…...
SPI通信协议
SPI通信协议 简介通信原理通信原理SPI数据通信的流程可以分为以下几步:通信特性设备时钟时钟速率时钟极性跟相位SPI协议层通讯流程详解优点:缺点: DS1302 时钟实验控制寄存器日历、时钟寄存器寄存器说明 DS1302 读写时序软件功能实现 简介 SP…...
vue中如何实现商品多规格添加(后台商城管理系统)
在制作商城类的后台管理系统中会遇到多规格商品的添加,需要向固定的数组内添加,通过查看数据结构正确的向数组中添加数据。 如图: 功能需求:1.每次点击添加新规格时,批量设置会多出来一行表格和一个标题输入框。 最…...
智创 AI 新视界 -- AIGC 重塑广告行业的创新力量(16 - 7)
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
Runloop
假设你的项目中有关tableView,然后还有一个定时器timer在执行,定时器代码如下: var num 0override func viewDidLoad() {super.viewDidLoad()let timer Timer(timeInterval: 1,target: self,selector: #selector(self.run),userInfo: nil,r…...
代码随想录第40天
121. 买卖股票的最佳时机 class Solution:def maxProfit(self, prices: List[int]) -> int:cost, profit float(inf), 0for price in prices:cost min(cost, price)profit max(profit, price - cost)return profit122.买卖股票的最佳时机II class Solution:def maxPr…...
element plus table组件多选获取数据id
首先给table加上 selection-change"handleSelectionChange"事件 示例 <el-table selection-change"handleSelectionChange" stripe:data"data?.slice((currentPage3 - 1) * pageSize3, currentPage3 * pageSize3)" style"width: 100%…...
自动驾驶:百年演进
亲爱的小伙伴们😘,在求知的漫漫旅途中,若你对深度学习的奥秘、JAVA 、PYTHON与SAP 的奇妙世界,亦或是读研论文的撰写攻略有所探寻🧐,那不妨给我一个小小的关注吧🥰。我会精心筹备,在…...
STM32 了解OLED
内容扩展 调试方式串口调试:通过串口调试,将调试信息发送到电脑端,电脑使用串口助手显示调试信息 显示屏调试:直接将显示屏连接到单片机,将调试信息打印到显示屏上 keil调试模式:借助Keil软件的调试模式&a…...
NanoLog起步笔记-7-log解压过程初探
nonolog起步笔记-6-log解压过程初探 再看解压过程建立调试工程修改makefile添加新的launch项 注:重新学习nanolog的README.mdPost-Execution Log Decompressor 下面我们尝试了解,解压的过程,是如何得到文件头部的meta信息的。 再看解压过程 …...
python连接redis详细步骤
1.下载并安装windows python Window 平台安装 Python: 以下为在 Window 平台上安装 Python 的简单步骤。 打开 WEB 浏览器访问 Python Releases for Windows | Python.org : 记得勾选 Add Python 3.6 to PATH。 在cmd 执行pip install redis 2.编辑python代码…...
使用redis 的stream 做消息中间件 多线程消费消息
1.redis stream 特点 1.支持消息持久化 2.消费者组模式 3.消息确认机制 4. 消息重试机制 5. 死信队列2. 消息生产者服务 2.1 如下代码Service Slf4j public class StreamMessageProducer {Autowiredprivate StringRedisTemplate redisTemplate;private static final String S…...
《操作系统 - 清华大学》6 -5:局部页面置换算法:最不常用置换算法 (LFU, Least Frequently Used)
文章目录 1. 最不常用算法的工作原理2.最不常用算法特征3. 示例 1. 最不常用算法的工作原理 最不常用算法:注意并不是表示算法本身不常用,而是采取最不常使用页面的策略,Least Frequently Used, LFU。LRU 是最久未被访问的页&…...
GWAS分析先做后学
大家好,我是邓飞。 GWAS分析是生物信息和统计学的交叉学科,上可以学习编程,下可以学习统计。对于Linux系统,R语言,作图,统计学,机器学习等方向,都是一个极好的入门项目。生物信息如…...
【threejs】创建FPS相机
原理说明 控制器是一个很麻烦的东西,需要创建更多的类来管理相机行为,并且可自定义性差,所以将部分控制器的功能绑定到相机上,可以解决这些问题,所以我以 FlyControls为例,将控制器功能绑定到相机上&#…...
路径规划之启发式算法之十一:布谷鸟搜索算法(Cuckoo Search,CS)
布谷鸟搜索算法(Cuckoo Search,CS)是一种新兴的自然启发式算法,由剑桥大学的杨新社教授和S.戴布(Xin-She Yang和Suash Deb)于2009年提出。该算法基于布谷鸟的寄生性育雏(巢寄生)行为…...
Mitel MiCollab企业协作平台存在任意文件读取漏洞(CVE-2024-41713)
免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...
java+springboot+mysql党务(党员)管理系统
项目介绍: 使用javaspringbootmysql开发的党务管理系统,系统包含管理员、用户角色,功能如下: 管理员:用户管理;党支部管理;党员管理(入党申请、积极分子、发展对象、预备党员、正式…...
gozero项目迁移与新服务器环境配置,包含服务器安装包括go版本,Nginx,项目配置包括Mysql,redis,rabbit,域名
迁移 **GoZero** 项目到新服务器并配置相关环境涉及多个步骤。以下是一个系统化的指南,涵盖服务器环境安装、数据库和缓存配置、项目部署以及域名绑定。 ### 步骤概述 1. **服务器环境配置** - 安装 Go 语言环境 - 安装 Nginx - 安装 MySQL 和 Redis -…...
使用WebDAV来上传和下载文件
WebDAV是什么 基于Web的分布式编写和版本控制(WebDAV)是超文本传输协议(HTTP)的扩展,有利于用户间协同编辑和管理存储在万维网服务器文档。WebDAV 由互联网工程任务组的工作组在 RFC 4918 中定义。许多现代操作系统为 …...
集成运算放大电路反馈判断
集成运算放大电路 一种具有很高放大倍数的多级直接耦合放大电路,因最初用于信号运算而得名,简称集成运放或运放 模拟集成电路中的典型组件,是发展最快、品种最多、应用最广的一种 反馈 将放大电路输出信号的一部分或全部通过某种电路引回到输…...
什么是绩效文化?
绩效文化是一种组织文化,它将绩效视为核心价值观,贯穿于组织的各个层面和活动之中。 一、绩效文化的内涵 目标导向 绩效文化强调组织成员都朝着共同的目标努力。这个目标通常是明确、可衡量的,如企业的年度利润目标、市场份额增长目标等。例…...
【力扣】409.最长回文串
问题描述 思路解析 因为同时包含大小写字母,直接创建个ASCII表大小的桶来标记又因为是要回文子串,所以偶数个数的一定可以那么同时,对于出现奇数次数的,我没需要他们的次数-1,变为偶数,并且可以标记出现过…...
android studio创建虚拟机注意事项
emulator 启动模拟器的时候,可以用 AVD 界面,也可以用命令行启动,但命令行启 动的时候要注意,系统有两个 emulator.exe ,建议使用 emulator 目录下的那个!! 创建类型为google APIs的虚拟机可从…...