【データ構造ブラッシング問題】消える数字と回転する配列

目次

1. 欠けている数字 

方法 1: すべての要素の XOR

方法 2: 等差数列を使用して、配列のすべての要素の合計を合計します。

2. 回転配列

質問タイプ 1: 文字列内の k 個の文字を左回転できる関数を実装します。

書き方1:暴力的な解決策

質問に従って右回転を書きます

書き方2:3段階回転法(左逆順、右逆順、全体逆順) 

左回転に従って右回転を書きます

質問タイプ 2: ある文字列が、別の文字列が回転された後の文字列であるかどうかを判断する関数を作成します。

方法1: strcmp関数に従って2つの文字列のascllコード値を比較します。

方法 2: strstr 関数で検索する

質問タイプ 3: 整数配列 nums を指定して、配列内の要素を右に k 位置回転します。ここで、k は非負の数です。

アイデア 1: 実際、これは右利きの弦であり、上記の最初の質問の左利きの弦に似ています。

アイデア2:3段階回転法(左部分と右部分を把握する)

アイデア 3: 時間のためのスペース


複雑さ に関する 演習:
1. 消えた数字
2. アレイを回転します

この記事では、数字が消えることと、配列が回転する問題の解決方法について説明します。

1. 欠けている数字 

アイデア:

元の質問の例 2、[9,6,4,2,3,5,7,0,1] から始めて、0 を追加すると 9 つの数字になりますが、0 には何もないため、0の位置が欠落した数字になります元の質問は、配列には からまでのすべての整数がnums含まれており、この配列の要素の値が連続的に増加しており、各増分が 1 であることを示しています。元の配列の最大の要素は 9 なので、1 から数えると、不足している数は 8 になります。0n

方法 1: すべての要素の XOR

アイデア:

具体的な方法は、0 から n までのすべての数値 (例 2 では 9) と nums (例 2 では配列) の XOR を計算することであり、欠落している整数は XOR 処理中に見つかります。XOR は結合法則と交換法則を満たすため、最終結果は欠落した整数になります。

XOR 演算の規則は次のとおりです。

0 XOR ルール:
任意の数値 a について、XOR 0 の結果は a そのもの、つまり a ^ 0 = a になります。

a ^ a ルール:
任意の数値 a について、XOR 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;
}

埋め込む:

方法 2: 等差数列を使用して、配列のすべての要素の合計を合計します。

等差数列の最初の 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;
}

埋め込む: 

2. 回転配列

質問タイプ 1: 文字列内の k 個の文字を左回転できる関数を実装します。

文字列内の k 個の文字を左回転できる関数を実装します。

例えば:

ABCD 1 文字を左回転して BCDA を取得します

ABCD 2 文字を左回転して 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 の場合、6 文字回転するごとに文字列は元の状態に戻るため、8 文字の回転は実際には 2 文字の回転と同じになります。したがって、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:3段階回転法(左逆順、右逆順、全体逆順) 

abcdef
b a c d e f 左部分回転
b af 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 1 文字を左回転して ABCDA を取得します

AABCD 2 文字を左回転して BCDAA を取得します

AABCD を 1 文字右に回転すると DAABC が得られます

方法1: strcmp関数に従って2つの文字列のascllコード値を比較します。

具体的な実装アイデアは次のとおりです。

ループ内でarr11 ビットを左に移動し、関数を使用してシフトされた合計が等しいかどうかを比較し、等しい場合は 1 を返し、左シフトによって変更できることを示します反复这个过程strcmparr1arr2arr2

全体を走査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判断する関数を実装しますarr1arr2

この例では、"abcdef"2 文字を左に移動することで変更できるため"cdefab"、最終的な出力は「yes」になります。

埋め込む:

 方法 2: strstr 関数で検索する

strstr についての説明:

strstrC 言語の文字列検索関数で、文字列内の別の文字列の出現位置を見つけるために使用されます。

この関数のプロトタイプは次のとおりです。char *strstr(const char *str1, const char *str2);

このうち、str1は検索対象の文字列、str2str1検索対象の文字列 in 、関数の戻り値はstr1in が最初に出現する箇所str2へのポインタで、str2出現しない場合は return しますNULL

を検索する場合str2、関数はstr1の先頭から文字を 1 つずつ比較し、一致しない文字が見つかると、最初に一致する部分文字列が見つかるか、内のすべての文字が比較されるまで、次の文字から一致を続けます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) は
        0 を返します。

arr1[]="abcdef" の前提で、arr2[]="cdef" であれば、

if (strstr(arr1, arr2)) は
            1 を返します。
        それ以外の場合は
            0 を返します。

関数は arr2 を arr1 の文字列とみなして 1 を直接返しますが、これは間違いです タイトルの意味は arr1 を左に回転できるかどうかです

この質問におけるこの関数の役割を簡単に説明してください。

strncat(arr1, arr1, len1);

この関数では、 の内容をコピーし、  の末尾に追加して新しい文字列を形成するstrncat(arr1, arr1, len1) 機能です 。これは、   a 内の一部の文字が最後にシフトされたときに先頭に表示される循環シフトの場合に対処するために行われます。 循環シフトは、コピー パスを最後に追加することで 通常のシフトに変換でき文字列比較のロジックが簡素化されますarr1arr1arr1arr1

コードの実行:

質問タイプ 3: 整数の配列が与えられた場合 nums、配列内の要素を k 1 位置だけ右に回転します。ここで、 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でも実際に実行できる荒技です。 

Leecode は次のように実行されます。 

 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:3段階回転法(左部分と右部分を把握する)

 コード:

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 関数はソース配列から宛先配列に 5 文字をコピーします。宛先配列の長さは 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]);
    }
}

 コードの実行:

 この章の最後に、間違いがある場合は修正してください。

おすすめ

転載: blog.csdn.net/weixin_65186652/article/details/131746645