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

简介: 【数据结构刷题】消失的数字和轮转数组(上)

复杂度的oj练习:

1.消失的数字

2.轮转数组

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


一.消失的数字


3c1ab75f33a84169af28cb99e473bb55.png

思路:

从原题的示例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。

画图:

212b111ab4bc4776b574ec28a8ef8ac5.png

所以说缺失的数字为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;
}


执行:

bf325eec60c24b8fa63dfeebe1a2c0aa.png


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


等差数列的前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;
}


执行:

6c3e17399b834885b461932fa83d5e39.png


二.轮转数组


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

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

例如:

ABCD左旋一个字符得到BCDA

ABCD左旋两个字符得到CDAB


写法1:暴力求解

思路:

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

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

image.gif

代码实现:

#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);
}


代码执行:

07ed7e181db24f77ab79972dbd77fcd9.png

特别说明:

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);
}


代码执行:

6b2ce91601794b70afbfad22ddf06655.png

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

abcdef

b   a   c   d   e   f左部分旋转

b   a   f    e   d   c右部分旋转

c   d   e    f   a   b整体逆序


image.gif

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

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

代码实现:

//左旋转
#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;
}


代码执行:

07ed7e181db24f77ab79972dbd77fcd9.png

根据左旋转写右旋转

代码实现:

右旋转
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;
}


代码执行:

8ac9e430ecc84ff3ad67a915febb218c.png

相关文章
|
2月前
|
存储 Java 程序员
数据结构之 - 深入了解数组数据结构
数据结构之 - 深入了解数组数据结构
48 6
|
29天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
55 5
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
60 4
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
48 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
2月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
29 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
218 9
|
1月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
37 1
|
1月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
1月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
1月前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
52 4