最近在读《剑指Offer》,在作者的GitHub上能找到全部源码,包含测试用例,但注释极少。光是读思路也是纸上得来终觉浅,自己拉到本地运行,一行行地解读一下。
算法一直是自己的弱项之一,不可马虎。
数组问题
数组的时间效率很高,可以根据下标在O(1)的时间读写任何元素;数组的空间效率不是很好,经常会有空闲的区域没有得到充分利用。
面试题3:数组中重复的数字
长度为n的数组中所有数字都在0~n-1的范围内,找出数组中任意一个重复的数字。
如果数组中没有重复的数字,那么从小到大排序后第i位置应是数字i。从头到尾扫描这个数组,扫描到下标i时,如果它的数字m不等于i就和下标m的数字比较重复,不重复就交换(以将其放置到正确位置上),如此反复。
#include<bits/stdc++.h>
using namespace std;
// 参数:
// numbers: 一个整数数组
// length: 数组的长度
// duplication: (输出) 数组中的一个重复的数字
// 返回值:
// true - 输入有效,并且数组中存在重复的数字
// false - 输入无效,或者数组中没有重复的数字
bool duplicate(int numbers[], int length, int* duplication) {
//nullptr是C++11中的空指针,这里是检查输入的有效性
if(numbers == nullptr || length <= 0)
return false;
//这里也是检查输入数组是否符合值都在0~n-1之间这一要求
for(int i = 0; i < length; ++i) {
if(numbers[i] < 0 || numbers[i] > length - 1)
return false;
}
//至此,合法性检查结束,遍历整个数组
for(int i = 0; i < length; ++i) {
//只要当前这个数和其下标不等,即m不为i,就一直循环
while(numbers[i] != i) {
//在交换之前,先判断一下它们是否重复(相等)
if(numbers[i] == numbers[numbers[i]]) {
//如果相等,就说明找到了这个重复元素,那么通过指针传出
*duplication = numbers[i];
//找到一个就结束该程序,因为题目就是只找任意一个
return true;
}
//至此,说明判断不相等,要将它们交换以将当前的m放在正确的位置
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
//如果当前这个数和下标相等,就去顺序地看下一个数
//如果数组里根本没有i这个数,那么在这个位置上的不断交换一定可以发现重复的数
}
//整个数组遍历完都没有return,此时已经全部有序(从0到n-1),没有重复的数
return false;
}
int main() {
int a[7]= {3,1,2,0,2,5,3};
int *p=new int();
//boolalpha不会将bool值转为1/0输出
cout<<boolalpha<<duplicate(a,6,p)<<endl;
if(nullptr==p)
cout<<"没有重复的数"<<endl;
else
cout<<"重复的数字是"<<*p<<endl;
delete p;
return 0;
}
面试题3增强:数组中重复的数字
要求不修改数组,来完成面试题3的要求。
这题在书上实际上是1~n-1的范围,这里还是改成0~n-1的范围,和上一题一样。
每次将范围二分,然后去看数组中这个范围内的数字是不是比这个范围的长度还大,如果是的话一定在这个范围内存在重复数字,如此缩小地来查找。该方法当找不到重复数字时,未必真的没有重复数字。
#include<bits/stdc++.h>
using namespace std;
// 参数:
// numbers: 一个整数数组
// length: 数组的长度
// start: 范围的起始值
// end: 范围的终止值
// 返回值:
// 数组中在指定范围内的数字的数目
int countRange(const int* numbers, int length, int start, int end)
{
//检查输入数组合法性
if(numbers == nullptr || length <= 0)
return 0;
//统计并返回数组中在指定范围内的数字个数
int count = 0;
for(int i = 0; i < length; i++)
if(numbers[i] >= start && numbers[i] <= end)
++count;
return count;
}
// 参数:
// numbers: 一个整数数组
// length: 数组的长度
// 返回值:
// 正数 - 输入有效,并且数组中存在重复的数字,返回值为重复的数字
// 负数 - 输入无效,或者数组中没有重复的数字
int getDuplication(const int* numbers, int length)
{
//检查输入数组合法性
if(numbers == nullptr || length <= 0)
return -1;
//初始考察的区间范围是0~n-1之间
int start = 0;
int end = length - 1;
//当这个区间还正确存在
while(end >= start)
{
//取区间的中间数,将其划分成两个子区间
int middle = ((end - start) >> 1) + start;
cout<<"middle="<<middle<<"\n";
//判断一下数组中落在前一半区间中的数的个数
int count = countRange(numbers, length, start, middle);
//终止条件:如果这个区间已经收缩到仅剩一个数
if(end == start)
{
//这时start=middle=end,刚刚统计的也就是数组中该数的个数
if(count > 1)//如果该数个数>1
return start;//那么所要求的重复数就是它
else//否则,没有找到重复的数
break;//直接退出寻找
}
//后面这部分是没有走[终止条件]时都会运行的
//如果数组中落在前一半区间的数的个数超过了区间长度
//说明至少存在一个该区间的数,在数组中是重复出现了的
if(count > (middle - start + 1))
end = middle;//因此将考察的范围缩小到这前半个区间上
else//否则
start = middle + 1;//将考察的范围缩小到后半个区间上
}
//运行至此,没有找到重复的数
//该算法不能保证真的没有重复的数,试想数组仅0113在0~3的区间上可被跳过
return -1;
}
int main(){
//从0~3区间内,数组中有2,2,3,3正好4个
//从4~7区间内,数组中有4,5,6,7正好4个
//识别不出这种情况下的重复数字
//这组数据在书上从1~n-1的情况下是能识别出来的
int a[8]={2,3,5,4,3,2,6,7};
cout<<getDuplication(a,8)<<"--------------------"<<endl;
//下面这组数据也找不出重复的1
int b[8]={0,1,1,3,4,5,6,7};
cout<<getDuplication(b,8)<<"--------------------"<<endl;
//这种数据就能找出来
int c[7]={3,1,2,0,2,5,3};
cout<<getDuplication(c,7)<<"--------------------"<<endl;
return 0;
}
面试题4:二维数组中的查找
在一个int型的二维数组中,每行从左到右递增,每列从上到下递增。任给一个整数,判断该二维数组中是否包含该数。
每次选取所剩二维数组的右上角数字。如果比要查找的数字大,因为该数字是本列最小,所以只要查看去掉该列所剩的左边的二维子数组;如果比要查找的数字小,因为该数字是本行最大,所以只要查看去掉该行所剩的下边的二维子数组。
#include<bits/stdc++.h>
using namespace std;
// 参数:
// matrix: 一个二维数组
// rows: 数组的行数
// columns: 数组的列数
// number: 要查找的数字
// 返回值:
// true表示找到了,false表示找不到
bool Find(int* matrix, int rows, int columns, int number)
{
bool found = false;//标识是否找到该数字
if(matrix != nullptr && rows > 0 && columns > 0)//输入合法性检查
{
//从数组右上角(0,columns-1)开始比较
int row = 0;
int column = columns - 1;
//行号在逐渐增大,列号在逐渐减小,谨防越界
while(row < rows && column >=0)
{
//row*columns+column就是二维数组中第row行第column列的数字
//如果这个数字和number一样,就已经找到它了
if(matrix[row * columns + column] == number)
{
found = true;//标识置为真
break;//退出循环
}
//如果这个数字比要找的数字大
else if(matrix[row * columns + column] > number)
-- column;//那么要刷去当前这一列,到左边的子二维数组去找
//如果这个数字比要找的数字小
else
++ row;//那么要刷去当前这一行,到下边的子二维数组去找
}
}
return found;
}
int main(){
//C++确定地初始化二维数组,则行数可以自动识别
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
cout<<boolalpha<<Find((int*)matrix, 4, 4, 7)<<endl;
return 0;
}