这是第一天==> 来自《剑指offer》
数组
1、最简单的数据结构
2、占据连续的内存,然后是按照顺序存储数据的
3、使用数组时,先创建,这个时候需要指定数组的容量大小,这样就根据大小分配内存了,计算机也不管你在这个内存中存储了多少数据,反正就是根据你指定的大小让你这个数据占据这么大的内存。所以数组会有以下缺点:第一个就是空间利用率不好,有时候就是会有空闲的区域占据了但是没有存储数据,没有被利用
4、因为是连续的,所以读/写数据的时间是O(1),时间效率可以说是很高了【一个特殊的hash表(键-值对)】
5、vector:一种动态数组,就是为了避免浪费,先为数组开辟较小的空间,之后再根据需要进行扩容(这个扩容的过程是,扩大为原来容量的两倍,之后把之前的数据复制到新的数组vector中,之后释放原来的内存),这个过程其实很消耗时间的
6、数据与指针:数组的名字就是一个指针,这个指针指向数组的第一个元素;还有就是C++并没有记录数组的大小,这就意味着我们访问数组中的元素时,要确定没有超过边界。
题目
数组中重复的数字
在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
OS:我就面试过那可怜的一两次,这。。复习的第一道题就是我遇到过的~~可以的~可以的?记得当时完全没有思路,就是先想到了用map计数,但是面试官说最好不要一开始就想到C++的STL,用STL一般都会占用很大内存,这道题完全不必要用这么大的“工程”,后面我有想到排序,但面试官好像不是很满意,就提示说可以让数据变为0,问我除了相减,还有什么能使相同的数据变为0,我回到了异或,但其实到现在我还是每相通怎么用异或做这道题。感受就是空间复杂度和时间复杂度是很重要的
思路:
1、排序:需要时间O(nlogn),从头到尾扫描排序后的数组(先排序+后扫描,这是两个操作)
2、hash表:时间O(n),空间O(n).emmmm,以空间换时间。就是
3、题目说的是数组中的数字都是再0~n-1,这。好像一开始就没被我重视过。这样一来,如果数组中没有重复的数字,那么当数组排序后数字i就只会出现在下标i的位置(所以还是得先排序),所以当有重复的数字时,那个重复的数字就不能再它应在的下标的位置了。举个例子:{2,3,1,0,2,5,3}
①比较第0个:a[0]=2,所以把它和a[2]=1调换 ==> {1,3,2,0,2,5,3}
②再比较第0个:a[0]=1,所以把它和a[1]=3调换 ==> {3,1,2,0,2,5,3}
③再比较第0个:a[0]=3,所以把它和a[3]=0调换 ==> {0,1,2,3,2,5,3}
④再比较第0个,a[0]=0,终于不用换了,a[1]=1……a[3]=3
⑤比较a[4]=2,这个时候,已经比较到第4个位置了,也就是说第二个位置a[2]=2了,所以2就是第一个出现重复的数字
每个元素(是说值,不是说某个下标)最多只要交换两次就能到达自己应到的位置,所以时间为2*n,O(n);空间复杂度的话,它没有开启新的空间,还是利用自己原来占有的空间,所以是O(1)
下面根据思路先自己写一遍代码。之后看看答案:
class Solution {
public:
bool duplicate(int numbers[], int length, int* duplication) {
int temp;
for(int k=0;k<length;k++)
{
if(numbers[i]<0 || numbers[i]>length-1)
return false;
}
//下面开始是自己写的部分,虽然通过了测试。但是发现没有测试异常情况就是输入的数值不在0~n-1之间,所以就根据参考答案加了判断部分
if (numbers != nullptr && length > 1) {
for (int i = 0; i < length; i++) {
while ((temp=numbers[i]) != i) {
if (numbers[i] == numbers[numbers[i]]) {
*duplication = numbers[i];
return true;
}
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
}
return false;
}
};
不修改数组找出重复的数字
在一个长度为n+1的数组里所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3
思路
1、粗暴的:创建一个辅助数组,之后把原数组中的所有元素都复制到这个辅助数组中,在这个过程中把元素m复制到下标为m的位置(复制过去之前要进行判断,默认为0,如果这个位置不是默认的0,那就是重复了) 空间复杂度O(n)
2、用一种类似二分查找法的方法:举个例子{2,3,5,4,3,2,6,7}——长度为8的数字,都在1~7范围内,统计1~4有5个,所以1~4中有重复的,1~2有2个,3~4有3个,再接着统计3、4各自出现的次数
先想好测试用例
代码:
int countNum(const int* numbers, int begin, int end, int length)
{
if (numbers != nullptr) {
int count = 0;
for (int i = 0; i < length; i++)
if (numbers[i] >= begin && numbers[i] <= end)
count++;
return count;
}
return 0;
}
//因为数组是不可以改变的,所以是const
int getDuplication(const int* numbers, int length)
{
for (int i = 0; i < length; i++)
if (numbers[i]<1 || numbers[i]>length - 1)
return -1;
if (numbers != nullptr && length > 1)
{
int start = 1;//最小的元素
int end = length - 1; //最大的元素
while (end >= start) {
int middle = ((end - start) >> 1 + start); //>>1表示“÷2”
int count = countNum(numbers, start, middle,length);
if (end == start)
{
if (count > 1)
return start;//就是说这有两个一样的数
else
return -1; //不包含重复数字
}
if (count > (middle - start) + 1)//4-1+1
end = middle; //1~4
else
start = middle+1; //5~8
}
return -1;
}
}
二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否包含该整数
思路
首先选取数组右上角的数字。如果该数字等于要查找的数组,则查找过程结束;如果该数字大于要查找的数字,则剔除这个数字所在的列;如果该数字小于要查找的数字,则剔除所在的行。这样每一步都可以缩小查找范围(可以自己画个图分析)
代码:
bool Find(int target, vector<vector<int> > array)
{
if (array.begin() != array.end()) {
int row = array.size();
int col = (*array.begin()).size();
int n = 0;
int m = col-1;
while(n<row && m>=0){
if (array[n][m] == target)
return true;
else if (array[n][m] > target)
--m; //抛弃这一列
else
++n; //抛弃这一排
}
}
return false;
}
代码出错了,运行不通过,测试用例不通过,但是很迷,就是自己看不出来【eeee~~是大小比较那里错了,也是醉了,粗心得一波】