【数据结构刷题】消失的数字和轮转数组

news2025/7/10 16:19:19

目录

一.消失的数字 

方法一:异或全部元素

方法二:利用等差数列求和-该数组全部元素之和。

二.轮转数组

题型1:实现一个函数,可以左旋字符串中的k个字符。

写法1:暴力求解

根据该题写出右旋转

写法2:三步旋转法(左逆序,右逆序,整体逆序) 

根据左旋转写右旋转

题型2:写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。

方法1:根据strcmp函数比较两者字符串的ascll码值。

方法2:通过strstr函数查找

题型3:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

思路1: 实际上就是右旋字符串,跟上面第一题的左旋字符串有着异曲同工之妙。

思路2:三步旋转法(搞清楚左部分和右部分)

思路3:空间换时间


复杂度的 oj 练习:
1.消失的数字
2.轮转数组

这篇文章是关于消失的数字和轮转数组的做题方法讲解。

一.消失的数字 

思路:

从原题的示例2入手, [9,6,4,2,3,5,7,0,1] ,加上0就是9个数,但是0是什么都没有,所以说0这个位置就是缺的数字。那么原题是说:数组nums包含从0n的所有整数,说明这个数组的元素的值是连续递增的,而且每次递增1。又因为原数组最大的元素为9,那么从1数过去,缺失的数字就是8。

方法一:异或全部元素

思路:

具体做法是将0到n(示例二的9)和nums(示例二的数组)中的所有数都异或起来,缺失的整数会在异或的过程中被找出来。异或满足结合律和交换律,因此最终的结果就是缺失的整数。

异或运算的规则如下:

0异或规则:
对于任意一个数a,a异或0的结果都是a本身,即a ^ 0 = a。

a ^ a规则:
对于任意一个数a,a异或a的结果是0,即a ^ a = 0。

 画图:

所以说缺失的数字为8。

代码实现:

int missingNumber(int* nums, int numsSize)
{
	int x = 0;
	for (int i = 0; i < numsSize; i++)
	{
		x ^= nums[i];
	}
	for (int i = 0; i < numsSize+1; i++)
	{
		x ^= i;
	}

	return x;
}

int main()
{
	int num[9] = { 9,6,4,2,3,5,7,0,1 };
	int numsSize = sizeof(num)/sizeof(num[0]);
    int ret=missingNumber(num, numsSize);
	printf("消失的数字为:%d", ret);
	return 0;
}

执行:

方法二:利用等差数列求和-该数组全部元素之和。

等差数列的前n项和公式为:

Sn = n/2 * (a1 + an)

其中,Sn表示等差数列的前n项和,a1表示等差数列中第一项的值,an表示等差数列中第n项的值,n表示等差数列的项数。根据等差数列的前n项和公式,可以快速求得等差数列前n项的和。

缺失的数字=Sn - 数组nums的每一个值

 代码实现:

int missingNumber(int* nums, int numsSize)
{
	int x = (1+numsSize)*numsSize/2;

	for (size_t i = 0; i < numsSize ; i++)
	{
		x -= nums[i];
	}

	return x;
}
int main()
{
	int num[9] = { 9,6,4,2,3,5,7,0,1 };
	int numsSize = sizeof(num)/sizeof(num[0]);
    int ret=missingNumber(num, numsSize);
	printf("消失的数字为:%d", ret);
	return 0;
}

执行: 

二.轮转数组

题型1:实现一个函数,可以左旋字符串中的k个字符。

实现一个函数,可以左旋字符串中的k个字符。

例如:

ABCD左旋一个字符得到BCDA

ABCD左旋两个字符得到CDAB

写法1:暴力求解

思路:

取出数组第一个元素放到临时变量tmp内,接着所有元素往前挪动,挪动完毕把tmp内的元素放到数组最后一个位置。

 以下是动图演示:这是左旋1个字符的效果

代码实现: 

#include<string.h>
void reverse_left(char *arr,int k)
{
	int len = strlen(arr);
	//k = k % len;
	for (int i = 0; i < k; i++)//k是左旋几遍的意思
	{
		int j = 0;
		char tmp = arr[0];
		for (j = 0; j < len - 1; j++)
		{
			arr[j] = arr[j + 1];//元素往前挪动
		}
		arr[len - 1] = tmp;
	}
}
int main()
{
	char arr[] = "abcdef";
	int k = 0;
	scanf("%d",&k);//左旋k个字符串
	reverse_left(arr, k);
	printf("%s", arr);
}

代码执行:

特别说明:

k = k % len 这一行代码的作用是将 k 对字符串的长度 len 取模 (求余数),从而确保旋转的位移量始终在字符串长度内。
例如,如果字符串长度为 6,k 的值为 8,则旋转 8 个字符实际上等于旋转 2 个字符,因为每旋转 6 个字符,字符串就会回到原始状态。因此,通过对 k 取模,可以确保旋转的位移量始终在字符串长度内,从而确保旋转的正确性。

根据该题写出右旋转:

//右旋转
void right_move(char arr[], int k)
{
	int i = 0;
	int len = strlen(arr);
	k = k % len;
	for (i = 0; i < k; i++) {
		int j = 0;
		char tmp = arr[len-1];
		for (j = len-1; j > 0; j--)
		{
			arr[j] = arr[j -1];
		}
		arr[0] = tmp;
	}
}
int main()
{
	char arr[20] = "abcdef";
	int k = 0;
	scanf("%d", &k);
	right_move(arr, k);

	printf("%s\n", arr);

}

代码执行: 

写法2:三步旋转法(左逆序,右逆序,整体逆序) 

abcdef
b   a   c   d   e   f左部分旋转
b   a   f    e   d   c右部分旋转
c   d   e    f   a   b整体逆序

最后一步实际上是从旋转完左子串和右子串之后整体再旋转一遍,

但为了效率地看出结果,只要按着箭头的方向输出字符就是最终的结果。 

代码实现:

//左旋转

#include<stdio.h>
#include<assert.h>
#include<string.h>
void reverse_string(char*left,char *right)
{
	assert(left && right);
	while (left < right)
	{
		char tmp = *left;
		*left = *right;
		*right = tmp; 
		left++;
		right--;
	}
}
void left_move(char arr[],int k)
{

	int len = strlen(arr);
	k = k % len;
	reverse_string(arr,arr+k-1);//左部分
	reverse_string(arr+k,arr+len-1);//右部分
	reverse_string(arr,arr+len-1);//整体
}
int main()
{
	char arr[] = "abcdef";
	int k = 0;
	scanf("%d",&k);
	left_move(arr, k);
	printf("%s\n", arr);
	return 0;
}

代码执行:

根据左旋转写右旋转

代码实现:

右旋转
void reverse_string(char* left, char* right)
{
	assert(left && right);
	while (left < right)
	{
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}
}
void right_move(char arr[], int k)
{
	int len = strlen(arr);
	k = k % len;
	reverse_string(arr+len-k, arr + len-1);//右部分
	reverse_string(arr , arr + len -1-k);//左部分
	reverse_string(arr, arr + len - 1);//整体
}
int main()
{
	char arr[] = "abcdef";
	int k = 0;
	scanf("%d", &k);
	right_move(arr,k);
	printf("%s\n", arr);
	return 0;
}

代码执行:

题型2:写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。

写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。

例如:给定s1 =AABCD和s2 = BCDAA,返回1

给定s1=abcd和s2=ACBD,返回0.

AABCD左旋一个字符得到ABCDA

AABCD左旋两个字符得到BCDAA

AABCD右旋一个字符得到DAABC

方法1:根据strcmp函数比较两者字符串的ascll码值。

具体实现思路是:

通过循环将arr1向左移动一位反复这个过程,并使用strcmp函数比较移动后的arr1arr2是否相等,如果相等则返回1,表示可以通过左移变成arr2

如果遍历完整个arr1后仍无法得到arr2,则返回0,表示无法通过左移变成arr2

int is_left_move(char arr1[], char arr2[])
{
	int len = strlen(arr1);
	int i = 0;
	for (i = 0; i < len; i++)//要是遍历完arr1数组,也没有出现和arr2相同的字符串那就返回0
	{
		char tmp = arr1[0];
		int j = 0;
		for (j = 0; j < len-1; j++)
		{
			arr1[j] = arr1[j + 1];
		}
		arr1[len-1] = tmp;
		if (strcmp(arr1, arr2) == 0)//比较ascll码值
		{
			return 1;
		}
	}
	return 0;
}
#include<stdio.h>
int main()
{
	char arr1[20] = "abcdef";//abcdefabcdef
	char arr2[] = "cdefab";
	int ret = is_left_move(arr1, arr2);
	if (ret == 1)
	{
		printf("yes\n");
	}
	else
	{
		printf("no\n");
	}
	return 0;
}

说明: 

该代码实现了一个函数is_left_move,用于判断一个字符串arr1是否能通过左移若干个字符的方式变成另一个字符串arr2

在这个例子中,"abcdef"可以通过左移两个字符的方式变成"cdefab",因此最终输出"yes"。

执行:

 方法2:通过strstr函数查找

关于strstr的说明:

strstr是C语言中的一个字符串查找函数,用于在一个字符串中查找另一个字符串的出现位置。

该函数的原型为:char *strstr(const char *str1, const char *str2);

其中,str1是要查找的字符串,str2是要在str1中查找的目标字符串,函数返回值是一个指向str1中第一次出现str2的指针,如果str2未出现,则返回NULL

在查找str2时,函数会从str1的开头开始逐个比较字符,一旦发现不匹配的字符,就会重新从下一个字符开始继续匹配,直到找到第一个匹配的子串或者str1中的所有字符都被比较完为止。如果str2为空字符串,则函数返回str1本身。

代码实现:

int is_left_move(char arr1[], char arr2[])
{
	int len1 = strlen(arr1);
	int len2 = strlen(arr2);
	if (len1 != len2)
		return  0;

	strncat(arr1, arr1, len1);
		if (strstr(arr1, arr2))
			return 1;
		else
			return 0;
}

#include<stdio.h>
int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "cdefab";
	int ret = is_left_move(arr1, arr2);
	if (ret == 1)
	{
		printf("yes\n");
	}
	else
	{
		printf("no\n");
	}
	return 0;
}

简述一下此函数在这题的作用:

if (len1 != len2)
        return  0;

在arr1[]="abcdef"前提下,如果arr2[]=“cdef”那么在

if (strstr(arr1, arr2))
            return 1;
        else
            return 0;

函数中就会认为arr2是arr1的字串,直接返回1,但这样是不对的,题目的意思是说arr1能否左旋转

简述一下此函数在这题的作用:

strncat(arr1, arr1, len1);

在这个函数中,strncat(arr1, arr1, len1) 的作用是将 arr1 的内容复制一遍添加到 arr1 的末尾,从而形成一个新的字符串。这样做是为了处理环形移位的情况,即当将 arr1 的某些字符移动到末尾时,它们将出现在开头。通过将 arr1 复制一遍添加到末尾,可以将环形移位转换为普通的移位从而简化字符串比较的逻辑

代码执行:

题型3:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

 思路1: 实际上就是右旋字符串,跟上面第一题的左旋字符串有着异曲同工之妙。

void rotate(int* nums, int numsSize, int k){
       int i=0;
       k=k%numsSize;
     for(i=0;i<k;i++)
     {
       int j=0;
       int tmp=nums[numsSize-1];
       for(j=numsSize-1;j>0;j--)
          {
          nums[j]=nums[j-1];
          }
          nums[0]=tmp;
     }

}

这种方式属于暴力求解,要用较多的时间,但实际上放到vs2019上可以执行 

力扣运行: 

 vs2019编译器:

#include<stdio.h>
void rotate(int* nums, int numsSize, int k) {
    int i = 0;
    k = k % numsSize;
    for (i = 0; i < k; i++)
    {
        int j = 0;
        int tmp = nums[numsSize - 1];
        for (j = numsSize - 1; j > 0; j--)
        {
            nums[j] = nums[j - 1];
        }
        nums[0] = tmp;
    }

}
int main()
{
    int nums[] = { 1, 2, 3, 4, 5, 6, 7 };
    int k = 0;
    scanf("%d", &k);
    int sz = sizeof(nums) / sizeof(nums[0]);
    rotate(nums, sz,  k);
    for (int i = 0; i < sz; i++) {
        printf("%d", nums[i]);
    }
}

代码执行:

思路2:三步旋转法(搞清楚左部分和右部分)

 代码实现:

void reverse(int*a,int left, int right)
{
    while (left < right)
    {
      int tmp = a[left];
        a[left] = a[right];
        a[right] = tmp;
        left++;
        right--;
    }
}
void rotate(int* nums, int numSize, int k)
{
    if (k > numSize)
        k %= numSize;
    reverse(nums, 0, numSize - k - 1);
    reverse(nums, numSize - k , numSize - 1);
    reverse(nums, 0, numSize  - 1);
}
int main()
{
    int nums[] = {1,2,3,4,5,6,7};
    int k = 0;
    scanf("%d", &k);
    int sz = sizeof(nums) / sizeof(nums[0]);
    rotate(nums, sz,  k);
    for (int i = 0; i < sz; i++) {
        printf("%d", nums[i]);
    }
}

思路3:空间换时间

思路:

简述一下memcpy的作用:

在 C 语言中,memcpy是一个函数,用于在内存之间复制一定数量的字节。其函数原型为:

void* memcpy(void* destination, const void* source, size_t num);

其中,destination 是目标内存区域的指针,source 是源内存区域的指针,num 是要复制的字节数。

memcpy 函数的作用是将源内存区域中的 `num` 个字节复制到目标内存区域中。如果源和目标内存区域重叠,行为是未定义的。

以下是一个示例:

#include <stdio.h>
#include <string.h>

int main() {
    char source[] = "hello";
    char destination[6];

    memcpy(destination, source, 6);

    printf("%s\n", destination);

    return 0;
}

输出结果为:

 

在这个示例中,memcpy 函数将 source 数组中的 5 个字符复制到 destination 数组中。由于 destination 数组长度为 6,所以需要复制 6 个字节,其中最后一个字节会被设置为 NULL 字节,即字符串结束标志。最终输出结果为 hello

memcpy 函数通常用于需要将一个内存区域的数据复制到另一个内存区域的情况。例如,当你需要对一个字符串进行操作时,可以使用 memcpy 函数将其复制到一个缓冲区中进行处理,以避免改变原始字符串的值。此外,memcpy 函数还可以用于将任何类型的数据复制到内存中。

思路3代码实现:

#include<stdio.h>

void rotate(int* nums, int numSize, int k)
{
    if (k > numSize)
        k %= numSize;
    int* tmp = (int*)malloc(sizeof(int) * numSize);
    memcpy(tmp+k,nums,sizeof(int)*(numSize-k));
    memcpy(tmp,nums + numSize - k, sizeof(int) * (k));
    memcpy(nums, tmp, sizeof(int) * (numSize));
    free(tmp);
    tmp=NULL;
}
int main()
{
    int nums[] = {1,2,3,4,5,6,7};
    int k = 0;
    scanf("%d", &k);
    int sz = sizeof(nums) / sizeof(nums[0]);
    rotate(nums, sz,  k);
    for (int i = 0; i < sz; i++) {
        printf("%d", nums[i]);
    }
}

 代码执行:

 本章完,如有错误欢迎大佬指正。

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

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

相关文章

2023年7月字节前端青训营入营题目记录(大题)

前言&#xff1a; 不一定是完整的题目内容&#xff0c;但意思差不多是一个意思 1.实现一个url解析成对象的函数&#xff1a; function ParseParams(url: string): Record<string, any> {const paramsstr url.split("?")[1];const paramsArr paramsstr.spl…

IDEA中把导航栏的字体放大

IDEA中如何把导航栏的字体放大&#xff1f; 选择File--Settings--Appearance&#xff0c;找到下面的size,选择自己想要的字体大小后点击 OK 即可。 字体大了果然看的更舒服了~写代码都有动力了哈哈哈哈 服了~

MySQL基本语句

目录 一、MySQL数据库管理 查看数据库信息 查看数据库中的表信息use 数据库名 #切换到书库中 显示数据表的结构&#xff08;字段&#xff09; 二、SQL语句 1.创建新的数据库 2.创建新的表 3.增加&#xff08;insert&#xff09; 4.删除 4.1清空表 4.2删除表 5.修改…

SpringMVC的数据响应-直接回写json字符串

一般我们操作对象&#xff0c;将对象转变为json 这时导入json 转换工具的包 包1 包2-json数据绑定 包3 返回的就是json字符串你直接返回就行了 返回一个json格式的字符串 直接回写就加这个res.... 内部字符串要进行相应的转意 能够看到json字符串 能不能你封装对象&#xff0c…

jenkins+python+pytest+selenium 自动化执行脚本并发送报告

目录 安装jenkins jenkins 安装网址&#xff1a; 傻瓜式安装 配置环境 键path 或者随便填 构建后操作 在jenkins上展示html的报告&#xff0c;需要添加一个HTML Publisher plugin插件 查看报告显示丢失了css样式 加载css样式丢失解决&#xff1a;https://www.cnblogs.com/…

如何使用Dom4J解析XML文档

文章目录 XML解析的方式使用Dom4J解析XML文档Dom4J结合XPath解析XML 最近在手写MyBatis的源码&#xff0c;在写到XMLConfigBuilder的时候&#xff0c;其中要解析xml文件构建Configuration。在MyBatis的早期版本中使用了DOM4J来解析配置文件和映射文件。但是从3.x版本开始,MyBat…

数据结构——绪论

基本概念 数据&#xff1a;数据是信息的载体&#xff0c;对客观事物的字符表示。 数据元素&#xff1a;数据的基本单位&#xff0c;通常作为一个整体进行考虑和处理。 数据项&#xff1a; 一个数据元素由多个数据项组成&#xff0c;数据项是数据元素不可分割的最小单位。 数据…

流程图如何制作?几个流程图实用制作方法教给你

流程图如何制作&#xff1f;流程图是一种重要的图表类型&#xff0c;通常用于描绘系统、流程或程序的步骤和关系。它们在各种领域都有广泛的应用&#xff0c;包括工程、科学、商业和教育等。本文将介绍一些制作流程图的实用方法&#xff0c;以及一些快速、易于使用的工具。 制作…

k8s之Pod容器资源限制

目录 一、Pod 容器的资源限制二、CPU 资源单位三、内存资源单位四、为本地临时性存储设置请求和限制五、总结 一、Pod 容器的资源限制 当定义 Pod 时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是 CPU 和内存大小&#xff0c;以及其他类型的资源。 当为…

MySQL索引事务与存储引擎

MySQL索引事务与存储引擎 索引概念索引作用副作用索引场景创建索引原则索引分类 事务概念:特性事务隔离级别 MYSQL存储引擎概念常用的存储引擎MyISAMInnoDB 索引 概念 是一个排序的列表&#xff0c;存储着索引值和这个值所对应的物理地址无须对整个表进行扫描&#xff0c;通过…

Vant安装及必坑

vant官网地址 Vant 4 - A lightweight, customizable Vue UI library for mobile web apps. 1.通过npm安装&#xff1a; 注意vue2和vue3不同&#xff0c;版本过高会报错 vue2 npm i vantlatest-v2或者npm i vant -Svue3 npm i vant或者npm i vantnext -S 备注&#xff1a…

为什么信创国产化替代 必备 “开放式ETL产品“(下篇)

信创国产化通俗来讲&#xff0c;就是在核心芯片、基础硬件、操作系统、中间件、数据服务器等领域实现信创产业的国产替代。ETL技术 属于基础软件类中间件技术。发展自己研制的安全可靠的能够保证国家信息安全的设备&#xff0c;随着信息安全问题日益突出&#xff0c;信息安全已…

小程序 点击view内部元素 不传参

点击 内部图片和文字 type 得到的是空 无法传递参数 解决办法: 用 currentTarget 代替 target

【LangChain】数据连接(Data connection)

概要 许多LLM申请需要特定于用户的数据&#xff0c;这些数据不属于模型训练集的一部分。 LangChain 为您提供了通过以下方式加载、转换、存储和查询数据的构建块&#xff1a; Document loaders &#xff1a; 从许多不同来源加载文档Document transformers&#xff1a;拆分文档…

怎么把文档翻译成英文?这几款文档翻译工具都能实现

听说你正在寻找免费的文档翻译软件&#xff0c;我来给你一些建议吧&#xff01;翻译软件的确是个很有用的工具&#xff0c;能够帮助我们快速翻译各种语言的文档。而且现在有很多免费的选择&#xff0c;真是再好不过了&#xff01;但是随着市面上文档翻译软件数量的增多&#xf…

车载测试:车联网功能组件及安全测试策略

目录 一、车联网功能组件 车域网 IVI TBOX ECU TSP APP 通信及密码特殊指标 车端特殊指标 APP特殊指标 测试用例 一、车联网功能组件 车联网是以汽车智能化、网联化为基础&#xff0c;广泛应用新一代通信技术、人工智能技术构建起的新型基础设施。在整体架构上&…

单据小票打印模板自定义设计,手机收银软件APP搭配蓝牙便携打印机,移动便携打印零售单单据小票

单据小票打印模板自定义设计&#xff0c;手机收银软件APP搭配蓝牙便携打印机&#xff0c;移动便携打印零售单单据小票&#xff0c;轻松实现仓库条码管理&#xff0c;扫码入库出库盘点_哔哩哔哩_bilibili单据小票打印模板自定义设计&#xff0c;手机收银软件APP搭配蓝牙便携打印…

突破平凡:创造独特而吸引人的登陆页UI设计灵感

今天&#xff0c;我们从移动APP产品经理或者UI设计师的角度再来聊一聊APP登录设计方式和如何去设计这些有意思的APP登录模块。 1、熟悉目前常见的手机APP登陆方式 ① 账号登陆&#xff08;手机、邮箱&#xff09; ② 第三方登陆&#xff08;微信&#xff0c;QQ&#xff0c;微博…

Java编程-IDEA中Java的main方法、sout快捷键设置

目的 我打出psvm这四个字母时&#xff0c;可快速打出main方法 我打出syso,sout时&#xff0c;可快速打出System.out.println(); 步骤&#xff1a; 1、打开IDEA&#xff0c;点击文件&#xff0c;选择Editor中的 Live Templates选项&#xff0c;点击右侧边栏中的 号 2、选中…

quartus工具篇——Signal Tap

文章目录 quartus工具篇——Signal Tap1、Signal Tap简介2、操作步骤3、查看波形结果4、总结 quartus工具篇——Signal Tap 1、Signal Tap简介 Quartus中的Signal Tap是一种用于FPGA设计调试和分析的工具。它可以捕获和显示设计中的信号波形&#xff0c;帮助设计人员验证设计…