剑指offer 学习笔记 数组

int GetSize(int data[]) {
	return sizeof(data);
}

int main() {
	int data[5] = { 1,2,3,4,5 };
	cout << sizeof(data) << endl;
	int *p = data;
	cout << sizeof(p) << endl;
	cout << GetSize(data) << endl;
	return 0;
}

以上代码输出为20 4 4,sizeof(data)求的是数组大小,p虽然和数组名一样能代表指向第一个元素的指针,但本质上它只是指针,在32位下占4字节。C/C++中当数组作为函数的参数传递时,数组自动退化为同类型的指针,因此函数返回值为4。

面试题3:数组中重复的数字
(1)在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。

方法一:将数组排序,然后从排序的数组中可以很快找到重复数字。排序一个长度为n的数组需要O(nlogn)的时间。

方法二:哈希表,从头到尾顺序扫描数组,每扫描到一个元素时,都可以用O(1)的时间判断哈希表中是否已经有该数字,如果没有就将这个数字加入哈希表,如果哈希表里已经存在该数字,就找到。此算法时间复杂度为O(n),但它提升的时间效率是以一个大小为O(n)的哈希表为代价的。

方法三:从头到尾依次扫描数组中每个数字,当扫描到下标为i的数字m时,首先比较i和m是否相等,如相等,则扫描下一个数字,如不相等,将下标为m的数字与m进行比较,如果相等,则找到一个重复数字(此数字在下标m和i处都出现了),如不相等,就把下标是m的数字和m交换,这样m就到了属于它的位置。接下来继续重复比较、交换这个过程,直到找到重复数字:

#include <iostream>
using namespace std;

bool duplicate(int nums[], int length, int* duplication) {
	if (length <= 0 || nums == nullptr) {
		return false;
	}
	
	for (int i = 0; i < length; ++i) {
		if (nums[i] > (length - 1) || nums[i] < 0) {    // 如果下标为i的值超出了题目范围
			return false;
		}
	}
	
	for (int i = 0; i < length; ++i) {
		while (nums[i] != i) {    // 如下标和数字值不相同
			if (nums[i] != nums[nums[i]]) {
				int temp = nums[i];
				nums[i] = nums[temp];
				nums[temp] = temp;    // 交换下标为i的值和下标为nums[i]的值
			} else {    // 当下标为i的值与下标为nums[i]的值相等
				*duplication = nums[i];
				return true;
			}
		}
	}
	return false;
}

int main() {
	int data[5] = { 0,1,0,0,3 };
	int duplication = -1;
	if (duplicate(data, sizeof(data) / sizeof(*data), &duplication)) {
		cout << "重复的数字为:" << duplication << endl;
	}
	return 0;
}

总的时间复杂度为O(n),空间复杂度为O(1)。

(2)在一个长度为n+1的数组里,所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。

方法一:创建一个长度为n+1的辅助数组,然后逐一把数字复制到辅助数组。如果被复制的数字是m,那么就把它复制到辅助数组下标为m的位置,如果这个位置已经有值,那么就能发现重复的数字了。这个方案需要O(n)的辅助空间,时间复杂度为O(n)。

方法二:把从1~n的数字从中间的数字m分为两部分,前面一半为1~m,后面一半为m+1~n。如果1~m的数字数目超过m,那么这一半的区间里一定包含重复的数字,否则另一半m+1~n的区间里一定包含重复的数字:

#include <iostream>
using namespace std;

int countRange(int nums[], int length, int start, int end) {
	int count = 0;
	for (int i = 0; i < length; ++i) {
		if (nums[i] >= start && nums[i] <= end) {    // 遍历数组,找到指定数字范围内的数字个数
			++count;
		}
	}
	return count;
}

int dup(int nums[], int length) {
	if (length < 0 || nums == nullptr) {    // 保证nums[]中的数字在1~n范围内并且nums不为空指针
		return -1;
	}
	
	int start = 1;    // 最小值为1
	int end = length - 1;    // 最大值为n
	while (start <= end) {
		int middle = start + ((end - start) >> 1);
		int count = countRange(nums, length, start, middle);
		if (start == end) {
		    if (count > 1) {    // 若此时count>1说明找到了重复数字
		        return start;
		    } else {    // 若此时count也为1,说明输入数据有误,这也是为什么不在开始检查输入数组是否合法
		        break; 
		    }
		}
		if (count > middle - start + 1) {    // 如果start和middle范围内的数字个数大于start和middle之间的数(包括两端),则重复的数字在此范围内
			end = middle;
		} else {    // 否则说明重复数字在后面范围内,注意此处必须用else而不能重新写一个if语句,重新写if有可能造成两个if判断都失败
			start = middle + 1;
		}
	}
}

int main() {
	int data[6] = { 2,1,4,3,5,5 };
	int length = sizeof(data) / sizeof(*data);
	int duplication = dup(data, length);
	if (duplication != -1) {
		cout << "重复数字为" << duplication << endl;
	}
	return 0;
}

这种方法按二分法思路,countRange函数被调用O(logn)次,每次需O(n)时间,总的时间复杂度为O(nlogn),空间复杂度为O(1),与第一种方法相比,相当于以时间换空间。并且这个方法不能找出所有的重复数字。

二维数组的查找:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序,请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数:

方法:首先选取数组中右上角的数字,如果该数字等于要查找的数字,则查找过程结束;如果该数字大于要查找的数字,则剔除这个数字所在的列;如果该数字小于要查找的数字,则剔除这个数字所在的行:

#include <iostream>
using namespace std;

bool Find(int* matrix, int columns, int rows, int number) {
	if (matrix == nullptr || columns <= 0 || rows <= 0) {    // 如果传入的是空数组或行列数小于0,返回false
		return false;
	}

	int row = 0;
	int column = columns - 1;

	while (row < rows && column >= 0) {
		if (matrix[row * columns + column] > number) {    // 如果目标数小于右上角
			--column;    // 去掉最后一列
		} else if (matrix[row * columns + column] < number) {    // 如果目标数大于右上角
			++row;    // 去掉最上面一行
		} else {
			return true;
		}
	}

	return false;
}

int main() {
	int nums[9] = { 1,2,3,4,5,6,7,8,9 };
	cout << Find(nums, 3, 3, 8) << endl;
	return 0;
}
posted @   epiphanyy  阅读(4)  评论(0编辑  收藏  举报  
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示