《剑指Offer》第五章

26 篇文章 1 订阅
4 篇文章 0 订阅

第一题:数组中出现次数超过一半的数字

仔细分析一下,这个数字必定是数组的中位数。可以利用快排的思想

int MoreThanHalfNum(vector<int> &nums)
{
	int length = nums.size();
	int middle = length >> 1;
	int start = 0;
	int end = length - 1;
	int index = Partition(nums, start, end);
	while (index != middle)
	{
		if (index > middle)
		{
			end = index - 1;
			index = Partition(nums, start, end);
		}
		else
		{
			start = index + 1;
			index = Partition(nums, start, end);
		}
	}
	return nums[index];
}
或者还有一个更好的算法,如果数组中一个数字出现次数超过一半,那么就是说它出现的次数比所有数字出现次数和还要多。因此我们可以在遍历数组的时候保存两个值:值与次数,遇到相同次数加1,否则减1,如果次数为0,则放弃当前,保存下一个。

int MoreThanHalfNum(vector<int> &nums)
{
	int value = nums[0];
	int count = 1;
	for (int i = 1; i < nums.size(); i++)
	{
		if (nums[i] == value)
			++count;
		else
			--count;
		if (count == 0)
		{
			value = nums[i];
			count = 1;
		}
	}
	return value;
}

第二题:最小的k个数

这一题同样可以用快排思想来解,如果基于数组的第k个数字来调整,使得比第k个数字小的都在数组的左边。和上面一题同样解法,只不过把middle 换成k-1

另一种解法是用容器保存最小的k个数,如果已有的数据小于k个,则执行插入操作。如果已经有k个数,则执行替换操作。

void GetLeastNumbers(vector<int> &nums, int k)
{
	multiset<int, greater<int> > leastNums;
	for (int i = 0; i < nums.size(); i++)
	{
		if (leastNums.size() < k)
			leastNums.insert(nums[i]);
		else
		{
			multiset<int, greater<int> >::iterator it = leastNums.begin();
			if (nums[i] < *it)
			{
				leastNums.erase(it);
				leastNums.insert(nums[i]);
			}
		}
	}
	multiset<int, greater<int> >::iterator begin = leastNums.begin();
	multiset<int, greater<int> >::iterator end = leastNums.end();
	while (begin != end)
	{
		cout << *begin;
		begin++;
	}
	cout << endl;
}
第三题:连续子数组最大和

这一题leetcode上也有,记住当前sum与历史sum,如果当前sum小于0,那么重置当前sum为当前数,否则进行相加操作。如果当前sum大于历史sum,则赋值。

int FindGreatestSumOfSubArray(vector<int> &nums)
{
	int cSum = 0;
	int GreatestSum = INT_MIN;
	for (int i = 0; i < nums.size(); i++)
	{
		if (cSum < 0)
			cSum = nums[i];
		else
			cSum += nums[i];
		if (cSum > GreatestSum)
			GreatestSum = cSum;
	}
	return GreatestSum;
}

第四题:从1到n整数中1出现的次数

从1到n整数中1出现的次数 这个博客的解法比书上要好的多。

第五题:把数组排成最小的数

书上的解法是将数字转换为字符串,然后定义排序规则,即AB与BA的大小进行比较来排序A和B,关键在于字符串一些函数的运用,比如sprintf函数,strcpy、strcat、strcmp函数

第六题:丑数,我们把只包含因子2、3和5的数称为丑数,求按小到大的顺序第1500个丑数

如何判断一个数是否为丑数:如果一个数能被2整除,我们就把它连续除以2,如果能被3整除,就连续除以3,如果能被5整除,连续除以5,最后如果是1,则为丑数否则不是。

bool IsUgly(int number)
{
	while (number % 2 == 0)
		number /= 2;
	while (number % 3 == 0)
		number /= 3;
	while (number % 5 == 0)
		number /= 5;
	return (number == 1) ? true : false;
}

基于这个判断,我们可以按照顺序判断每一个数是否为丑数,时间复杂度不够好。我们考虑一个数组,数组存储的是当前的丑数,以后的每个丑数,都是用之前的数组的元素相乘的来的。接下来就是如何得到后面的丑数并保证它是有序的。可以想到,数组的第一个元素是1,1与2 3 5分别相乘,可以得到三个值,这三个值里面最小的,肯定就是下一个丑数的最大值,接着max2的下标后移,继续比较。

void mkUglyNumber(){
    gArr[top++] = 1;
    int *max2 = gArr;
    int *max3 = gArr;
    int *max5 = gArr;
    while(top < 1500){
        int min = getMin(*max2*2,*max3*3,*max5*5);
        gArr[top] = min;
        while(*max2*2 <= gArr[top])
            ++max2;
        while(*max3*3 <= gArr[top])
            ++max3;
        while(*max5*5 <= gArr[top])
            ++max5;
        ++top;
    }
}
第七题:第一个只出现一次的字符

由于字符可与整数进行互换,且字符最多为256个,所以可以用一个大小为256的数组来记录字符出现的次数。此题较为简单。

第八题:数组中的逆序对

在数组中的两个数字如果前面一个数字大于后面的数字,则成为逆序对。此题可以利用归并排序来解,此博客比较详细 点击打开链接

第九题:两个链表的第一个公共结点

第一种方法是用栈来解决,先将两个链表分别入栈,然后逐一出栈比较,最后一个相同的即为第一个公共结点

第二种方法是先遍历两个链表,得到长度为m和n,先在长的链表上走m-n(假设m>n),再一起走,第一个相同的即为所求点

第三种可以用带环链表求解,因为两个链表相交,必定为Y型,可以变成带环链表







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值