【剑指offer】——关于数组的简单题练习

1、调整数组顺序使奇数位于偶数前面

1.1题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

示列:
输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。

提示:
1 <= nums.length <= 50000
1 <= nums[i] <= 10000

1.2题目分析
方法一:利用快慢指针

  1. 定义两个指针low和fast,都从下标为0的位置开始
  2. 只要fast没有数组越界,fast指针一直向前遍历
  3. 遇到low指针的元素是偶数并且fast指向的元素是奇数就交换这两个指针所指向的值
  4. 只有遇到low指向的元素不是偶数的时候,low指针才往前遍历

以例子数组【1234】来说,具体的过程如下:
在这里插入图片描述
代码实现:

void swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}
vector<int> exchange(vector<int>& nums)
{
	int low = 0;
	int fast = 0;
	while (fast < nums.size())
	{
		if (nums[low] % 2 == 0 && nums[fast] % 2 != 0)
		{
			swap(nums[low], nums[fast]);
		}
		if (nums[low] % 2 != 0)
		{
			low++;
		}
		fast++;
	}
	return nums;
}

方法二:双指针

  1. 定义两个指针low和high。low从数组的头部开始找偶数,high从数组的尾部开始找奇数
  2. 直到low>=high时退出寻找

代码实现:

vector<int> exchange1(vector<int>& arr)
{
	int low = 0;
	int high = arr.size() - 1;
	while (low < high)
	{
		if (arr[low] % 2 != 0)
		{
			low++;
			continue;
		}
		if (arr[high] % 2 == 0)
		{
			high--;
			continue;
		}
		swap(arr[low], arr[high]);
	}
	return arr;
}
int main()
{
	vector<int> res;
	vector<int> a;
	res.push_back(1);
	res.push_back(2);
	res.push_back(3);
	res.push_back(4);
	a = exchange1(res);
	for (int i = 0; i < a.size(); i++)
	{
		cout << a[i] << endl;
	}
	return 0;
}

2、在排序数组中查找数字|

1、题目要求
统计一个数字在排序数组中出现的次数。

示例1
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例2
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

2、题目分析
方法一:挨个遍历查询
这样做的时间复杂度最高,可达O(n)

int search(vector<int>& nums, int target)
{
	int count = 0;
	for (int i = 0; i < nums.size(); i++)
	{
		if (nums[i] == target)
			count++;
	}
	return count;
}

方法二:巧用二分查找
基于上一个方法时间复杂度太高,并且没有充分利用数组排序的特点,所以采用二分查找的方式来求解,时间复杂度是O(logn)

  1. 定义两个指针,low指向数组的头部,high指向数组的尾部
  2. 利用二分查找的方式,判断数组中间mid所指的数字是否和目标元素相同
  3. 如果相同的话,利用数组排序的特点,目标元素的相邻元素一定和目标元素是相同的,分别在左右子数组中统计和目标数字相同的数字个数Lcount和Rcount
  4. 最后返回值为Lcount+Rcount+1

具体的,以下面以 nums = [5,7,7,8,8,10], target = 8这个例子加以分析
在这里插入图片描述
注意!!
还有特殊情况的处理,如果数组中没有元素应该return -1

int search1(vector<int>& arr, int target)
{
	if (arr.size() == 0)
	{
		return -1;
	}
	int low = 0;
	int high = arr.size() - 1;
	int countL = 0;
	int countR = 0;
	int count = 0;
	while (low <= high)
	{
		int mid = (high - low + 1) / 2 + low;
		if (arr[mid] < target)
			low = mid + 1;
		else if (arr[mid] > target)
			high = mid - 1;
		else//找到了,因为是排序数组,则相同的元素就在该元素的左右两边
		{
			int Lhigh = mid ;
			int Rlow = mid ;
			while (Lhigh > low&& arr[--Lhigh] == target)
			{
				countL+=1;
			}
			while (Rlow < high && arr[++Rlow] == target)
			{
				countR+=1;
			}	
			count = countL + countR + 1;
			break;
		}
	}
	return count;
}

3、0~n-1中缺失的数字

1、题目要求
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例1:
输入: [0,1,3]
输出: 2

示例2:
输入: [0,1,2,3,4,5,6,7,9]
输出: 8

限制:1 <= 数组长度 <= 10000

2、题目分析
注意!
:[0,1]表示数组长度为n-1=2,所以n=3,共有3个数字,所以在{0,3}之间的是[0,1,2];不能理解为只有2个数字,不缺失数字。这一点很重要。
方法一:两次循环

  1. 第一次循环得到0~n-1的总和sum1,第二次循环得到数组元素的总和sum2
  2. 返回sum1-sum2,即为缺失的那个数
  3. 但是这种方法不可取,时间复杂度太高,没有充分利用数组有序的特点
    代码实现:
int missingNumber(vector<int>& arr) 
{
	if (arr.size() == 0)
		return -1;
	int sum1 = 0;
	int sum2 = 0;
	for (int i = 0; i <= arr.size(); i++)
	{
		sum1 += i;
	}
	for (int j = 0; j < arr.size(); j++)
	{
		sum2 += arr[j];
	}
	return sum1 - sum2;
}

方法二:一次循环

  1. 根据数组排列有序的特点。没有缺失的数字一定是和其下标一一对应的
  2. 如果遇到没有对应的数字则表示该下标对应的数字缺失
  3. 如果数组遍历完毕都没找到缺失的数字,说明是数组最后一个数字缺失。返回arr.size()的值

代码实现如下:

int missingNumber1(vector<int>& arr)
{
	for (int i = 0; i < arr.size(); i++)
	{
		if (i != arr[i])
			return i;
	}
	return arr.size();
}

方法三:二分查找

  1. 二分查找的思路还是根据下标和数组中的值来判断缺失元素。但是相比于方法三更好的是一次可以舍弃一半的数据

  2. 如果==mid等于arr[mid]==的值相等的话,表示在mid之前都是按照顺序没有缺失存放的,那么就在后半边数组中去寻找,high = mid+1.

  3. 如果mid和arr[mid]的值不相等的话。比较mid-1和arr[mid-1]的值是否相等
    3.1如果相等就表示mid即为缺失的数字
    3.2如果不相等,则表示缺失的值,在mid前面。让high = mid-1

  4. 查找到最后返回值的探讨。返回值不应该是mid。比如下面这个例子

    在这里插入图片描述
    所以二分查找跳出循环过后需要返回low,而不是mid.

代码实现如下:

int missingNumber2(vector<int>& arr)
{
	if (arr.size() == 0)
		return -1;
	int low = 0;
	int high = arr.size() - 1;
	while(low <= high)
	{
		int mid = (high - low + 1) / 2 + low;
		if (mid == arr[mid])//左半部分没有问题
		{
			low = mid + 1;
		}
		else//左半部分有问题
		{
			if (low > 0 && mid - 1 == arr[mid - 1])
				return mid;
			else
				high = mid - 1;
		}
	}
	return low;

}

int main()
{
	vector<int> res;
	res.push_back(0);
	res.push_back(1);
	res.push_back(2);
	res.push_back(3);
	res.push_back(4);
	res.push_back(5);
	cout << missingNumber2(res) << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值