剑指offer----C语言版----第一天

目录

1. 数组中重复的数字Ⅰ

 1.1 题目描述

1.2 思路一

1.3 思路二

 1.4 思路三(最优解)


1. 数组中重复的数字Ⅰ

原题:剑指 Offer 03. 数组中重复的数字 - 力扣(LeetCode)https://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/

 1.1 题目描述

给你一个数组,大小为N,数组里面的值的介于 [ 0, N-1 ],要求你找出数组中任意一个重复的数字。

1.2 思路一

 我们选用一个时间复杂度为O(N*logN)的排序算法对数组进行排序,例如快速排序,堆排序,归并排序。然后再去遍历排序好的数组,找出重复的数字。解题的时间复杂度:O(N*logN),空间复杂度:O(1)。

我这里自己写了快速排序的代码,如果大家嫌麻烦可以使用C语言的库函数qsort来对数组进行排序,底层逻辑也是快速排序,但是在数据量比较小的时候用的是插入排序,库函数嘛,效率的优化是最高的。

cplusplus.com - The C++ Resources Networkhttps://legacy.cplusplus.com/大家可以使用这个来查看C语言的标准库函数。

//数字交换
void Swap(int* pa, int* pb)
{
    int temp =*pa;
    *pa = *pb;
    *pb = temp;
}

//快速排序优化算法:三数取中间
int GetMidIndex(int* arr, int left, int right)
{
	int mid = (left + right) >> 1;
	if (arr[mid] > arr[left])
	{
		if (arr[left] > arr[right])
		{
			return left;
		}
		else if (arr[mid] > arr[right])
		{
			return right;
		}
		else
		{
			return mid;
		}
	}
	// arr[mid] < arr[left]
	else
	{
		if (arr[right] > arr[left])
		{
			return left;
		}
		else if (arr[mid] > arr[right])
		{
			return mid;
		}
		else
		{
			return right;
		}
	}
}



void QuickSort(int* arr, int left,int right)
{
	if (left >= right)
	{
		return;
	}
	int index = GetMidIndex(arr, left, right);
	int begin = left;
	int end = right;
	Swap(&arr[index], &arr[begin]);
	int pivot = begin;
	
	int key = arr[begin];
	while (begin < end)
	{
		//找小的
		while (begin < end && arr[end] >= key)
		{
			--end;
		}
		arr[pivot] = arr[end];
		pivot = end;
		//找大的
		while (begin < end && arr[begin] <= key)
		{
			++begin;
		}
		arr[pivot] = arr[begin];
		pivot = begin;
	}
	pivot = begin;
	arr[pivot] = key;

    QuickSort(arr, left, pivot - 1);
    QuickSort(arr, pivot + 1, right);

}

int findRepeatNumber(int* nums, int numsSize){
    assert(nums);
    int left = 0;
    int right = numsSize - 1;
    QuickSort(nums, left, right);
    int i = 0;
    for(i = 0; i < numsSize - 1; i++)
    {
        if(nums[i] == nums[i + 1])
        {
            return nums[i];
        }
    }
    return -1;
}

剑指offer----C语言版----第一天_第1张图片

1.3 思路二

我们可以利用一个简单的哈希表:开辟一个同样大小的数组arr,并将数组arr初始化为0,然后遍历原来的数组nums,将nums数组的值对应到arr相应下标的位置,让他的值加一,如果再遍历过程中遇到arr的值大于1,则找到了该重复的数字。该解题方法的时间复杂度:O(N),空间复杂度O(N),以空间换时间。

int findRepeatNumber(int* nums, int numsSize){
    int* arr = (int*)calloc(numsSize, sizeof(int));
    int i = 0;
    for(i = 0; i < numsSize; i++)
    {
        arr[nums[i]] += 1;
        if(arr[nums[i]] > 1)
        {
            free(arr);
            arr = NULL;
            return nums[i];
        }
    }
    free(arr);
    arr = NULL;
    return -1;
}

剑指offer----C语言版----第一天_第2张图片

 1.4 思路三(最优解)

我们分析题目可以得到如下规律:数组大小是N,数值范围是N-1,如果说该数组没有重复的元素,那么数组中每一个下标都可以对应与其下标相同的数字,如果说有重复的数字,那么,会出现一个下标有多个与其下标相等的数字,或者出现没有与其下标相等的数字。

利用这一特性,我们可以遍历该数组,扫描到下标 i 时,首先比较这个数 m 与下标 i 是否相等,如果相等,继续扫描下一个元素。如果不相等,将m与下标为 m 的数进行比较,如果相等则找到一个重复的数字,如果不相等将 m 交换到下标为 m 的位置,对交换过来的数字继续重复上述判断即可,直到找到重复的数字。

解题的时间复杂度:O(N),空间复杂度O(1)。

下面以一组例子来演示方便理解:

剑指offer----C语言版----第一天_第3张图片

void Swap(int* pa, int* pb)
{
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

int findRepeatNumber(int* nums, int numsSize){
    int i=0;
    for(i = 0; i < numsSize; i++)
    {
        while(nums[i] != i)
        {
            if(nums[i] == nums[nums[i]])
            {
                return nums[i];
            }
            Swap(&nums[i],&nums[nums[i]]);
        }
    }
    return -1;
}

 剑指offer----C语言版----第一天_第4张图片

你可能感兴趣的:(剑指offer,算法,c语言,leetcode)