周小伦说的
建议王道的所有算法题最好都写一下啊,尤其是树的,排序相关的要写一下,然后还有链表,链表有一些反转链表啊
一些经典的代码肯定要背的呀,比如说,三种遍历的递归和非递归,怎么找树的宽度对吧啊,找树的宽度,这是层次便利的对吧,层次便利的东西。然后找就树的宽度一定会找,然后然后啊找就是每个哪个节点在第几层,对吧?这个东西你你也会找,当然这个其实就是dfs就能搞得定。然后呃,除了数的,除了这些之外,
你还比如说排序对吧?你像你快排肯定会写吧,对吧?然后还有冒泡。我觉得堆牌堆牌其实可以不用会,就是快牌和堆牌,你会一个就可以了,其实我觉得背快牌是最好背的。像龟并还,也没有快排好写对吧?快速排序应该是最好写的,然后像链表那边就是被几个经典的,比如说什么怎么反转一个链表对吧?啊,或者说这个反转列表有两种嘛,
一个是可以允许你开辅助空间的,一个是要原地反转,就是要用o1的空间法则把它反转出来。这这些肯定都要,这些都是最基本的,肯定要会吧,对吧?然后像这种经典的双人就是,然后还有除此之外就是把所有期末题里面考核的算法都把它整理一下啊,都记一记,因为考试可能会考到类似的题目啊,会可能会考到非常类似的题目。所以说就是大家这些基本代码都会背的话,你考试的时候基本上也能写点东西上去了,我感觉。
你像很多关数的题目,只要你能够把整棵树遍历完,对吧?什么东西求不出来的,可顶多就是你的算法,它并不是非常优美。对吧,你都能把整个树遍历完了,你还找不到什么祖先了,对吧?那肯那肯定能找得到啊,你遍历树的时候你就能知道每个节点的祖先是什么,对吧?那你把所有所有节点的祖先全存下来,那不就能找到?
找到那两个点的公共祖先了吗?反正就是只要你能够,就很多东西,只要你能够把这个东西遍历完很多,结果都能够找得到,这是最简单一个问题,比如说这个题目你不知道双指针对吧?但是你肯定知道,你可以写两重放循环嘛,你写两重放循环考试的时候12分怎么也给你个八分吧,对吧?那你总不总不可能一个字不写嘛?你像这个找这个对吧?怎么可能?你像这个找找叶子键的个数对吧?
只要你会写后续便利,就只要你会写任何一种便利。你只要把那个便利写出来,然后把里面把里面加上,就是说它的左子数为空的时候把结果加一,你这道题就是满分啊。对吧,但就是最暴力的写法也能得80%以上的分数啊,考试的时候因为它其实不会考,你不会给你卡特别严的,它主要考你数据结构。又不是又不是面试,对吧?就是又不是面试,一定要你写出一个什么显法度的东西来。
#include
int FindSum(int A[], int n, int key) {
int i = 0, j = n - 1, sum; // 初始化双指针i和j,分别指向数组的开头和结尾
// sum用于记录当前两个数的和
while (i != j && A[i] + A[j] != key) { // 当i和j不相遇且两个数的和不等于key时循环
sum = A[i] + A[j]; // 计算当前两个数的和
if (sum < key)
i++; // 如果和小于key,将i指针右移一位
else
j--; // 如果和大于key,将j指针左移一位
}
if (i == j)
return 0; // 如果i和j相遇,说明没有找到满足条件的数对,返回0
else {
printf("%d和%d\n", A[i], A[j]); // 输出找到的满足条件的数对
}
return 1; // 返回1表示找到了满足条件的数对
}
int main() {
int A[] = {1, 2, 4, 6, 7, 11}; // 已经排好序的数组
int n = sizeof(A) / sizeof(A[0]); // 数组的长度
int key = 11; // 目标数字
FindSum(A, n, key); // 调用函数查找满足条件的数对
return 0;
}
//输出:4和7
这段代码的思路是使用双指针法来查找满足条件的两个数。首先,定义两个指针i和j,分别指向数组的开头和结尾。然后,通过不断调整指针的位置来逼近目标值。在每一次循环中,计算当前指针所指向的两个数的和,并与给定的数字key进行比较。如果和小于key,则将i指针右移一位;如果和大于key,则将j指针左移一位。直到找到满足条件的两个数或者i和j相遇为止。
如果找到了满足条件的两个数,即A[i] + A[j] == key,则输出这两个数。如果不存在这样的数对,即i和j相遇,说明没有找到满足条件的数对,返回0。最后,返回1表示找到了满足条件的数对。
// 定义单链表的节点结构
typedef struct ListNode {
int data; // 节点的数据
struct ListNode* next; // 指向下一个节点的指针
} LinkList;
LinkList findMid(LinkList head) {
LinkList p = head, q = head; // 定义两个指针p和q,初始时都指向链表的头节点head
while (q != NULL && q->next != NULL) { // 当快指针q不为空且q的下一个节点不为空时循环
p = p->next; // 慢指针p向后移动一步
q = q->next->next; // 快指针q向后移动两步
}
return p; // 返回慢指针p,即链表的中间节点
}
//考试不用写
int main() {
// 创建链表节点
LinkList* node1 = (LinkList*)malloc(sizeof(LinkList));
node1->data = 1;
LinkList* node2 = (LinkList*)malloc(sizeof(LinkList));
node2->data = 2;
LinkList* node3 = (LinkList*)malloc(sizeof(LinkList));
node3->data = 3;
LinkList* node4 = (LinkList*)malloc(sizeof(LinkList));
node4->data = 4;
LinkList* node5 = (LinkList*)malloc(sizeof(LinkList));
node5->data = 5;
// 构建链表
LinkList* head = node1;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = node5;
node5->next = NULL;
// 查找链表的中间节点
LinkList* midNode = findMid(head);
printf("链表的中间节点的值为:%d\n", midNode->data);
return 0;
}
// 结果输出:链表的中间节点的值为3
这个算法使用了快慢指针的思想来找到单链表的中间节点。定义两个指针p和q,初始时都指向链表的头节点head。每次循环中,慢指针p向后移动一步,快指针q向后移动两步。当快指针q到达链表末尾时,慢指针p正好指向链表的中间节点。
这个算法的时间复杂度为O(n),其中n是链表的长度。快指针每次移动两步,慢指针每次移动一步,所以快指针最多需要遍历整个链表的一半,因此时间复杂度为O(n)。
这个算法的空间复杂度为O(1),只使用了常数级别的额外空间。
typedef struct Node {
Elemtype data;
struct Node* next;
} Node;
typedef Node* Link;
// 获取链表的中间节点
Link getMiddleNode(Link head) {
if (head == NULL) { // 如果链表为空,直接返回NULL
return NULL;
}
Link p1 = head; // 快指针,每次移动两步
Link p2 = head; // 慢指针,每次移动一步
while (p1 && p1->next) { // 当快指针p1和p1的下一个节点都不为空时循环
p1 = p1->next->next; // 快指针p1每次移动两步
p2 = p2->next; // 慢指针p2每次移动一步
}
return p2; // 返回慢指针p2,即链表的中间节点
}
1)最简单思路:从头开始,依次比较相邻元素,当第一次出现前面的元素<后面的元素时,前面那个元素即为最小。
int findMin(int A[], int n){
int i = 0;
for(;i < n;i++){
if(A[i] > A[i+1]){
return A[i];
}
}
}
2)高级思路:运用二分法,当二分点处的值小于左右两点值时,继续二分
代码思路的解释如下:
int findMin(int A[], int n) {
int low = 0; // 左边界
int high = n - 1; // 右边界
while (low < high) {
int mid = (low + high) / 2; // 计算中间位置
if (A[mid] < A[mid-1] && A[mid] < A[mid+1]) { // 判断A[mid]是否为最小值
return A[mid]; // 返回最小值
}
else if (A[mid] > A[mid+1]) { // A[mid]大于A[mid+1],最小值在mid右侧
low = mid + 1; // 更新左边界
}
else { // A[mid]小于等于A[mid+1],最小值在mid左侧或就是mid
high = mid; // 更新右边界
}
}
return A[low]; // 返回最小值
}
方法二:gpt版本
int findMin(int A[], int n) {
int left = 0; // 左边界
int right = n - 1; // 右边界
while (left < right) { // 当左边界小于右边界时循环
int mid = left + (right - left) / 2; // 计算中间位置
if (A[mid] > A[mid + 1]) { // 中间元素大于右侧元素,最小值在mid右侧
left = mid + 1; // 更新左边界为mid+1
} else { // 中间元素小于等于右侧元素,最小值在mid左侧或就是mid
right = mid; // 更新右边界为mid
}
}
return A[left]; // 返回左边界位置对应的元素值,即为最小值
}
gpt代码思路步骤:
代码的思路是使用二分查找法来寻找序列中的最小值。具体步骤如下:
修正后的代码时间复杂度为O(logn),空间复杂度为O(1)。
int FindMax(int *A, int m) {
if (m == 0) return -1; // 如果数组大小为0,返回错误
int begin = 0;
int end = m - 1;
int MP = (begin + end) / 2;
while (MP > 0 && MP < m - 1) {
if (A[MP] > A[MP+1] && A[MP] > A[MP-1]) { // 如果符合条件就返回此值
return MP;
} else if (A[MP] < A[MP+1]) {
begin = MP + 1; // 在递增段
MP = begin + (end - begin) / 2;
} else {
end = MP - 1; // 在递减段
MP = begin + (end - begin) / 2;
}
}
if (MP == 0) return 0; // 如果数组是完全递减的,则第一个值就是最大值
if (MP == m-1) return m-1; // 如果数组是完全递增的,则最后一个值为最大值
return -1;
}
这个算法的时间复杂度为O(log m),因为每次循环都将搜索空间减半。
空间复杂度为O(1),因为只需要常数级别的额外空间来存储变量。
代码思路解释:
void NewSequence(int A[], int n) {
int i = 0, j = n - 1;
int temp;
while (i < n && j >= 0) { // 循环直到遍历完整个数组
while (i < n && A[i] % 2 == 0) { // 找到第一个奇数元素的位置
i += 2; // 奇数元素存放在奇数单元,所以每次跳两个位置
if (i >= n) { // 如果i超出数组范围,跳出循环
break;
}
}
while (j >= 0 && A[j] % 2 == 1) { // 找到第一个偶数元素的位置
j -= 2; // 偶数元素存放在偶数单元,所以每次跳两个位置
if (j < 0) { // 如果j小于0,跳出循环
break;
}
}
if (i < n && j >= 0) { // 如果i和j都在数组范围内,交换奇数元素和偶数元素
temp = A[i];
A[i] = A[j];
A[j] = temp;
}
}
}
// 第一轮循环完
// 8 2 5 7 6 10 9 1 4 3
// 第二轮循环完
// 8 2 10 7 6 5 9 1 4 3
// 第三轮循环完
// 8 9 10 7 6 5 2 1 4 3
// 第四轮循环完
// 跳出
int main() {
int a[10] = {3, 2, 5, 7, 6, 10, 9, 1, 4, 8}; // 初始化整数数组a
int n = 10; // 数组长度
for (int i = 0; i < n; i++)
printf("%d ", a[i]); // 打印初始数组元素
printf("\n");
NewSequence(a, n); // 调用函数对数组重新安排
for (int i = 0; i < n; i++)
printf("%d ", a[i]); // 打印重新安排后的数组元素
// 8 9 10 7 6 5 2 1 4 3
printf("\n");
return 0;
}
代码思路解释:
void sort(int A[], int n) {
int low = 0, high = n - 1; // 初始化低位指针和高位指针
int temp = A[low]; // 保存低位元素的值
while (low < high) { // 循环直到低位指针和高位指针相遇
while (low < high && A[high] % 2 == 0) // 从高位开始找到第一个奇数元素
high--;
A[low] = A[high]; // 将奇数元素移到低位
while (low < high && A[low] % 2 == 1) // 从低位开始找到第一个偶数元素
low++;
A[high] = A[low]; // 将偶数元素移到高位
}
A[high] = temp; // 将原来的低位元素放回数组
}
int main() {
int A[] = {3, 2, 5, 7, 6, 10, 9, 1, 4, 8}; // 初始化数组
int n = sizeof(A) / sizeof(A[0]); // 数组长度
printf("Original Array:\n");
for (int i = 0; i < n; i++)
printf("%d ", A[i]); // 打印初始数组元素
sort(A, n); // 调用函数对数组进行奇偶分割
printf("\nSorted Array:\n");
for (int i = 0; i < n; i++)
printf("%d ", A[i]); // 打印重新安排后的数组元素
printf("\n");
return 0;
}
Original Array:
3 2 5 7 6 10 9 1 4 8
Sorted Array:
1 9 5 7 3 10 6 2 4 8
代码的思路是使用两个指针low和high,分别指向数组的低位和高位。然后,通过两个嵌套的循环,分别找到第一个奇数元素和第一个偶数元素的位置,并将它们互换位置。循环过程中,指针low从低位开始向高位移动,指针high从高位开始向低位移动,以便继续寻找下一个奇数和偶数元素。最后,将原来的低位元素放回数组的最后一个位置,以保证奇数在前半部分,偶数在后半部分。
这段代码的时间复杂度为O(n),因为每个元素最多被访问两次,且只进行了常数次的交换操作。同时,代码只使用了常数个额外的存储单元,满足题目的要求。
修改后的BubbleSort函数使用的是冒泡排序算法。
冒泡排序算法的基本思想是通过相邻元素的比较和交换来实现排序。具体步骤如下:
冒泡排序算法的时间复杂度为O(n^2),其中n为链表的结点个数。由于每一轮内层循环都会将一个最大的元素移动到尾部,因此在最好的情况下,当链表已经有序时,算法的时间复杂度可以达到O(n)。
#include
struct NODE {
int data;
NODE* next;
};
void BubbleSort(NODE *&L) {
int i, count = 0, num; // 定义变量i、count、num
NODE *p, *q, *tail; // 定义指针p、q、tail
p = L; // 将p指向链表的头结点
while (p->next != NULL) { // 遍历链表,计算链表的结点个数
count++; // 结点个数加1
p = p->next; // 移动指针p到下一个结点
}
for (i = 0; i < count - 1; i++) { // 外层循环,控制比较的轮数
num = count - i - 1; // 记录内层循环需要的次数
q = L->next; // 将q指向链表的第一个结点
p = q->next; // 将p指向q的下一个结点
tail = L; // 将tail指向q前一个结点,方便交换和进行下一步操作
while (num--) { // 内层循环,控制每轮比较的次数
if (q->data > p->data) { // 如果当前结点的值大于后一个结点的值,则交换它们的位置
q->next = p->next;
p->next = q;
tail->next = p;
}
tail = tail->next; // 移动tail指针到下一个结点
q = tail->next; // 移动q指针到下一个结点
p = q->next; // 移动p指针到下一个结点
}
}
}
int main() {
// 创建一个带表头的线性链表
NODE* header = new NODE;
header->next = nullptr;
NODE* current = header;
// 向链表中添加元素(假设元素大于10000)
for (int i = 1; i <= 100000; i++) {
NODE* newNode = new NODE;
newNode->data = i;
newNode->next = nullptr;
current->next = newNode;
current = newNode;
}
// 对链表进行筛选,保留前100个最小的元素
BubbleSort(header);
// 输出筛选后的结果
NODE* p = header->next;
for (int i = 0; i < 100 && p != nullptr; i++) {
std::cout << p->data << " ";
p = p->next;
}
std::cout << std::endl;
// 释放链表内存
p = header;
while (p != nullptr) {
NODE* temp = p;
p = p->next;
delete temp;
}
return 0;
}
解析代码思路:
选择排序,每次选一个
【算法思想】在 DLinkList 类型的定义中添加 int freq 域,将所有节点的 freq 域均初始化为 0。在查找到 data 域为 x 的节点p 时,将其 freq 域增 1。再找到p节点的前驱节点pre,若pre 不是头节点,且满足 pre->freq < p->freq,则 pre 指针再向前移,直到找到一个节点pre,满足 pre->freq≥p->freq,则将p 节点移到pre 节点之后,如图所示,其操作是先删除p 节点,再将其插入到pre 节点之后。
#include
typedef int ElemType;
// 双向链表结点的定义
typedef struct DuLNode {
ElemType data;
int freq; // 访问频度
struct DuLNode *prior, *next; // 前驱和后继指针
} DuLNode, *DuLinkList;
// 在链表中定位结点并更新频度
int LocateNode(DuLinkList &h, ElemType x);
int main() {
DuLinkList head = new DuLNode; // 创建头结点
head->next = NULL;
head->prior = NULL;
head->freq = 0;
// 创建链表
DuLinkList node1 = new DuLNode;
node1->data = 1;
node1->freq = 0;
node1->prior = head;
head->next = node1;
DuLinkList node2 = new DuLNode;
node2->data = 2;
node2->freq = 0;
node2->prior = node1;
node1->next = node2;
DuLinkList node3 = new DuLNode;
node3->data = 3;
node3->freq = 0;
node3->prior = node2;
node2->next = node3;
node3->next = NULL;
// 测试 LocateNode 函数
int result = LocateNode(head, 2);
if (result == 1) {
std::cout << "找到并更新了结点" << std::endl;
} else {
std::cout << "未找到结点" << std::endl;
}
// 输出链表中每个结点的值和频度
DuLinkList p = head->next;
while (p != NULL) {
std::cout << "结点值:" << p->data << ",频度:" << p->freq << std::endl;
p = p->next;
}
// 释放链表内存
p = head;
while (p != NULL) {
DuLinkList temp = p;
p = p->next;
delete temp;
}
return 0;
}
// 找到并更新了结点
// 结点值:2,频度:1
// 结点值:1,频度:0
// 结点值:3,频度:0
// 在链表中定位结点并更新频度
int LocateNode(DuLinkList &h, ElemType x) {
DuLinkList p = h->next; // 从头结点的下一个结点开始遍历链表
DuLinkList q;
while (p != NULL && p->data != x) // 遍历链表,直到找到值为 x 的结点或者链表遍历结束
p = p->next;
if (p == NULL) // 如果没有找到值为 x 的结点
return 0;
else {
p->freq++; // 找到了值为 x 的结点,将其频度加1
q = p->prior; // q 为 p 的前驱结点
if (q != h) { // 如果 p 不是第一个数据结点
while (q != h && q->freq < p->freq) // 找到 q 结点,使得 q 的频度大于等于 p 的频度
q = q->prior;
p->prior->next = p->next; // 先删除 p 结点
if (p->next != NULL)
p->next->prior = p->prior;
p->next = q->next; // 将 p 结点插入到 q 结点之后
if (q->next != NULL)
q->next->prior = p;
q->next = p;
p->prior = q;
}
return 1;
}
}
代码思路解释:
时间复杂度:在最坏情况下,需要遍历整个链表,时间复杂度为 O(n),其中 n 是链表的长度。
空间复杂度:除了输入参数外,算法的空间复杂度为 O(1),即常数级别的额外空间使用。
int LocateNode(DLinkList * L, ElmeType x) {
DLinkList * p = L - > next, * pre;
while (p != NULL && p - > data != x)
p = p - > next; //找到 data 域值为 x 的结点 p;
if (p == NULL) //如果没有找到,返回
return 0;
else { //找到这样的结点
p - > freq++; //频度 freq +1
pre = p - > prior; //pre 为 p 的前驱结点
if (pre != L) {
while (pre != L && pre - > freq < p - > freq) //找到 pre 结点
pre = pre - > prior;
p - > prior - > next = p - > next; //删除结点 p
if (p - > next != NULL)
p - > next - > prior = p - > prior;
p - > next = pre - > next; //将结点 p 插入到 pre 结点之后
if (pre - > next != NULL)
pre - > next - > prior = p;
pre - > next = p;
p - > prior = pre;
}
return 1;
}
}
方法一:遍历
方法二:先排序再遍历
【算法思想】
A 与 B 的交集是指同时出现在两个集合中的元素,因此,此题的关键点在于:依次摘取两个表中相等的元素重新进行链接,删除其他不等的元素。
算法思想是:假设待合并的链表为 La 和 Lb,合并后的新表使用头指针 Lc(Lc 的表头结点设为 La 的表头结点)指向。pa 和 pb 分别是链表 La 和 Lb 的工作指针,初始化为相应链表的首元结点。从首元结点开始进行比较,当两个链表 La 和 Lb均为到达表尾结点时,如果两个表中的元素相等,摘取 La 表中的元素,删除 Lb表中的元素;如果其中一个表中的元素较小,删除此表中较小的元素,此表的工作指针后移。当链表 La 和 Lb 有一个到达表尾结点为空时,依次删除另一个非空表中的所有元素。最后释放链表 Lb 的头结点。
void Intersection(LinkList &La, LinkList &Lb, LinkList &Lc) {
// 初始化工作指针和结果链表
pa = La->next; // pa 是链表 La 的工作指针,初始化为首元结点
pb = Lb->next; // pb 是链表 Lb 的工作指针,初始化为首元结点
Lc = pc = La; // 用 La 的头结点作为 Lc 的头结点
// 遍历链表 La 和 Lb
while (pa && pb) {
if (pa->data == pb->data) {
// 相等,交集并入结果表中
pc->next = pa;
pc = pa;
pa = pa->next;
u = pb;
pb = pb->next;
free(u); // 释放链表 Lb 中的节点
} else if (pa->data < pb->data) {
// 删除较小者 La 中的元素
u = pa;
pa = pa->next;
free(u); // 释放链表 La 中的节点
} else {
// 删除较小者 Lb 中的元素
u = pb;
pb = pb->next;
free(u); // 释放链表 Lb 中的节点
}
}
// 处理剩余的节点
while (pa) {
// Lb 为空,删除非空表 La 中的所有元素
u = pa;
pa = pa->next;
free(u); // 释放链表 La 中的节点
}
while (pb) {
// La 为空,删除非空表 Lb 中的所有元素
u = pb;
pb = pb->next;
free(u); // 释放链表 Lb 中的节点
}
pc->next = NULL; // 设置结果链表尾节点的 next 指针为 NULL
free(Lb); // 释放链表 Lb 的头结点
}
代码的思路如下:
这段代码的目的是找到链表 La 和链表 Lb 的交集,并将结果存储在链表 Lc 中。
方法一:先找出两者的长度,长的链表先多走几步再同时走
算法设计思想:可以知道如果两个链表有公共节点,那么该公共节点之后的所有节点都是两个链表所共有的,所以长度一定也是相等的,如果两个连表的总长的是相等的,那么我们对两个连表进行遍历,则一定同时到达第一个公共节点。但是连表的长度实际不一定相同,所以我们只需要计算出来年各个链表的长度之差n,然后让长的那个表先移动n,短的那个表在开始遍历,这样它们一定同时到达打一个公共节点,我们只需要在向后移动的时候比较两个链表的节点是否相等就可以获得第一个公共节点。
判断两个链表是否交叉。如果交叉,则返回h1链表的交叉点;否则,返回NULL。
参数h1为第一个链表的头结点,h2为第二个链表的头结点。
// 数据结构定义
typedef struct LNode {
ElemType data; // 节点数据
struct LNode* next; // 指向下一个节点的指针
} LNode, *LinkList;
LinkList Judge(LinkList h1, LinkList h2) {
LinkList tp1 = h1, tp2 = h2; // 创建两个临时指针,用于遍历链表
int NodeNum1 = 0, NodeNum2 = 0; // 用于记录链表的节点个数
if (h1 == NULL || h2 == NULL) // 判断头指针是否为空
return NULL;
// 统计链表 1 的节点个数
while (tp1->next != NULL) {
NodeNum1++;
tp1 = tp1->next;
}
// 统计链表 2 的节点个数
while (tp2->next != NULL) {
NodeNum2++;
tp2 = tp2->next;
}
// 如果两个链表的最后一个节点不一样,说明两个链表无交叉结点
if (tp1 != tp2)
return NULL;
else { // 两链表有交叉点
int i;
tp1 = h1->next;
tp2 = h2->next;
if (NodeNum1 < NodeNum2) {
// tp1, NodeNum1中保存较长的链表信息
tp1 = h2->next;
tp2 = h1->next;
i = NodeNum1;
NodeNum1 = NodeNum2;
NodeNum2 = i;
}
for (i = 0; i < NodeNum1 - NodeNum2; i++)
tp1 = tp1->next;
while (tp1->next != NULL) {
if (tp1 == tp2)
return tp1; // 返回交叉节点的指针
else {
tp1 = tp1->next;
tp2 = tp2->next;
}
}
}
return NULL; // 返回结果,根据实际情况修改
}
#include
void PrintContinuousSequence(int small, int big); // 声明打印连续序列的函数
void FindContinuousSequence(int sum)
{
if(sum < 3) // 如果给定的和小于3,不存在连续序列,直接返回
return;
int small = 1; // 初始化连续序列的起始值为1
int big = 2; // 初始化连续序列的结束值为2
int middle = (1 + sum) / 2; // 连续序列的中间值,用于判断循环终止条件
int curSum = small + big; // 当前连续序列的和
while(small < middle) // 当起始值小于中间值时进行循环
{
if(curSum == sum) // 如果当前连续序列的和等于给定的和,打印该序列
PrintContinuousSequence(small, big);
while(curSum > sum && small < middle) // 如果当前连续序列的和大于给定的和,移动起始值
{
curSum -= small; // 减去起始值
small ++; // 起始值右移
if(curSum == sum) // 如果移动后的连续序列的和等于给定的和,打印该序列
PrintContinuousSequence(small, big);
}
big ++; // 结束值右移
curSum += big; // 加上新的结束值
}
}
void PrintContinuousSequence(int small, int big)
{
for(int i = small; i <= big; ++ i) // 循环打印连续序列
std::cout << i << " ";
std::cout << std::endl;
}
// ====================测试代码====================
void Test(const char* testName, int sum)
{
if(testName != nullptr)
std::cout << testName << " for " << sum << " begins: " << std::endl;
FindContinuousSequence(sum); // 调用函数查找和为给定值的连续序列
}
int main(int argc, char* argv[])
{
Test("test1", 1);
Test("test2", 3);
Test("test3", 4);
Test("test4", 9);
Test("test5", 15);
Test("test6", 100);
return 0;
}
在一个长度为n的数组里,所有元素都是0~n-1范围内的整数。某些元素在数组中可能重复出现,但不知道哪些是重复出现的,也不知道重复出现多少次。现要尽可能快地找出数组中所有重复出现的元素。请回答下列问题:
1)设计相关的数据结构。
2)描述求解问题的方法步骤,并说明时间和空间效率
这段代码的时间复杂度为O(n^2),因为它使用了两个嵌套的循环。如果数组长度较大,性能可能会较低。
#include
void find_same_number(int arr[], int len) {
int i, j;
for (j = 0; j < len - 1; j++) { // 遍历数组,从第一个元素到倒数第二个元素
for (i = j + 1; i < len; i++) { // 从当前元素的下一个元素开始,与后面的元素进行比较
if (arr[j] == arr[i]) {
printf("重复出现的数字:%d\n", arr[j]);
// 如果想要在找到重复元素后立即返回,可以使用 return 语句
// return;
}
}
}
}
int main(int argc, char const *argv[]) {
int arr[7] = {2, 3, 1, 0, 2, 5, 3};
int len = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
find_same_number(arr, len);
return 0;
}
这段代码使用数组a记录每个数字出现的次数,时间复杂度为O(n),空间复杂度为O(N)。
#include
#define N 1000
void find_duplicates(int num[], int n) {
int a[N] = {0}; // 初始化数组a,用于记录每个数字出现的次数
for (int i = 0; i < n; i++) {
if (!a[num[i]]) { // 如果a[num[i]]为0,说明该数字还未出现过
a[num[i]]++; // 将a[num[i]]加1,表示该数字出现了一次
} else if (a[num[i]] >= 1) { // 如果a[num[i]]大于等于1,说明该数字已经出现过
printf("重复出现的数字:%d\n", num[i]);
}
}
}
int main() {
int num[7] = {2, 3, 1, 0, 2, 5, 3};
int n = sizeof(num) / sizeof(num[0]); // 计算数组长度
find_duplicates(num, n);
return 0;
}
这段代码的时间复杂度为O(n),空间复杂度为O(n)。相比之前的代码,它使用了一个辅助数组b来记录数字出现的次数,通过空间换取了更快的速度。
#include
void find_duplicates(int num[], int n) {
int b[n]; // 创建长度为n的数组b,用于记录数字出现的次数
for (int i = 0; i < n; i++) {
b[i] = 0; // 初始化数组b,全部置为0
}
for (int i = 0; i < n; i++) {
b[num[i]]++; // 将数字num[i]作为下标,将对应位置的元素加1
}
for (int i = 0; i < n; i++) {
if (b[i] > 1) { // 如果b[i]大于1,说明数字i重复出现
printf("重复出现的数字:%d\n", i);
}
}
}
int main() {
int num[7] = {2, 3, 1, 0, 2, 5, 3};
int n = sizeof(num) / sizeof(num[0]); // 计算数组长度
find_duplicates(num, n);
return 0;
}
struct ListNode {
int data; // 节点存储的值
ListNode* left; // 左子节点指针
ListNode* right; // 右子节点指针
};
// 查找小于目标节点值的最大节点
ListNode* search(ListNode* cur, ListNode* p) {
ListNode* max = NULL; // 初始化最大节点指针为空
int num = p->data; // 获取目标节点的值,存储在变量 num 中
while (cur != NULL) { // 循环遍历链表,直到当前节点为空
if (cur->data < num) { // 如果当前节点的值小于目标节点的值
max = cur; // 更新最大节点指针为当前节点
cur = cur->right; // 向右子节点移动
} else {
cur = cur->left; // 如果当前节点的值大于等于目标节点的值,则向左子节点移动
}
}
return max; // 返回小于目标节点值的最大节点指针
}