名企算法与数据结构题目最优解—部分问题总结之七

最大的leftMax与rightMax之差的绝对值

题目

给定一个长度为N(N>1)的整型数组arr,可以划分为左右两个部分,左部分为arr[0…K],右部分为arr[K+1…N-1],K可以取值的范围是[0, N-2]。求这么多划分方案中,左部分中的最大值减去右部分最大值的绝对值中,最大是多少。
举例:[2, 7, 3, 1, 1],当左部分为[2, 7],右部分为[3, 1, 1]时,左部分最大值减去右部分最大值绝对值为4.左部分为[2,7,3],右部分为[1,1]时,左部分最大值减去右部分最大值绝对值为6.还有很多划分方案,但最终返回6.

代码

#include<iostream>
#include<algorithm>
using namespace std;

int maxAbs1(int* arr, int len)
{
	int res = INT_MIN;
	int maxLeft = 0;
	int maxRight = 0;
	for (int i = 0; i < len - 1; i++)
	{
		maxLeft = INT_MIN;
		for (int j = 0; j <= i; j++)
			maxLeft = max(arr[j], maxLeft);
		maxRight = INT_MIN;
		for (int j = i + 1; j < len; j++)
			maxRight = max(arr[j], maxRight);
		res = max(abs(maxLeft - maxRight), res);
	}
	return res;
}

int maxAbs2(int* arr, int len)
{
	int* lArr = new int[len];
	int* rArr = new int[len];
	lArr[0] = arr[0];
	rArr[len - 1] = arr[len - 1];
	for (int i = 1; i < len; i++)
		lArr[i] = max(lArr[i - 1], arr[i]);
	for (int j = len - 2; j >= 0; j--)
		rArr[j] = max(rArr[j + 1], arr[j]);
	int Max = 0;
	for (int i = 0; i < len - 1; i++)
		Max = max(Max, abs(lArr[i] - rArr[i + 1]));
	return Max;
}

//找到数组最大值之后,只需要确定最大值属于哪一部分,若属于左半部分
//只要右半部分最大值尽量小;如果是右半部分,则左半部分最大值尽量小即可
int maxAbs3(int* arr, int len)
{
	int Max = INT_MIN;
	for (int i = 0; i < len; i++)
		Max = max(arr[i], Max);
	return Max - min(arr[0], arr[len - 1]);
}

int main()
{
	int len;
	cin >> len;
	int* input = new int[len];
	for (int i = 0; i < len; i++)
		cin >> input[i];
	int res1 = maxAbs1(input, len);
	int res2 = maxAbs2(input, len);
	int res3 = maxAbs3(input, len);
	cout << res1 << " " << res2 << " " << res3 << endl;
	getchar();
	return 0;
}

路径数组变为统计数组

题目

给定一个路径数组paths,表示一张图,paths[i] =j 代表城市i连向城市j,如果paths[i] = i,则表示i城市是首都,一张图中只能有一个首都求途中除首都指向自己之外不会有环。如paths=[9, 1, 4, 9, 0 ,4, 8 , 9, 0, 1],如图所示在这里插入图片描述
由数组表示的图可知,城市1为首都,距离为0,离首都距离为1的城市只有9,离首都距离为2的城市有0,3,和7,离首都距离为3的城市有城市4和8,离首都距离为4的城市有2,5和6.所以距离0的城市有1座,距离为1的城市有1座,距离为2的城市有3座,距离为3的城市有2座,距离为4的城市有3座。那么统计数组为nums=[1,1,3,2,3,0,0,0,0,0],nums[i]==j表示距离为i的城市有j座。要求实现函数将路径数组paths在原数组上进行调整,使之成为nums数组。
若paths长度为N,时间复杂度要求为O(N),额外空间复杂度为O(1).

代码

#include<iostream>
#include<algorithm>
using namespace std;

void pathToDis(int* &paths, int len)
{
	int cap = 0;
	for (int i = 0; i < len; i++)
	{
		if (paths[i] == i)
			cap = i;
		else if (paths[i] > -1)
		{
			int curI = paths[i];
			paths[i] = -1;
			int preI = i;
			while (paths[curI] != curI)
			{
				if (paths[curI] > -1)
				{
					int nextI = paths[curI];
					paths[curI] = preI;
					preI = curI;
					curI = nextI;
				}
				else
					break;
			}
			int value = paths[curI] == curI ? 0 : paths[curI];
			while (paths[preI] != -1)
			{
				int lastPreI = paths[preI];
				paths[preI] = --value;
				curI = preI;
				preI = lastPreI;
			}
			paths[preI] = --value;
		}
	}
	paths[cap] = 0;
}

void disToNums(int* &disArr, int len)
{
	for (int i = 0; i < len; i++)
	{
		int index = disArr[i];
		if (index < 0)
		{
			disArr[i] = 0;  //important
			while (true)
			{
				index = -index;
				if (disArr[index] > -1)
				{
					disArr[index]++;
					break;
				}
				else
				{
					int nextIndex = disArr[index];
					disArr[index] = 1;
					index = nextIndex;
				}
			}
		}
	}
	disArr[0] = 1;
}

void pathsToNums(int* &paths, int len)
{
	if (paths == NULL || len < 1)
		return;
	pathToDis(paths, len);
	disToNums(paths, len);
}

int main()
{
	int len;
	cin >> len;
	int* path = new int[len];  //[9 1 4 9 0 4 8 9 0 1]
	for (int i = 0; i < len; i++)
		cin >> path[i];
	pathsToNums(path, len);
	for (int i = 0; i < len; i++)
		cout << path[i] << " ";
	cout << endl;
	getchar();
	return 0;
}

正数数组的最小不可组成和

题目

给定一个正数数组arr,其中所有的数为整数,一下是最小不可组成和的概念
1、把arr每个子集的所有元素加起来会出现很多之,最小记为min,最大记为max。
2、在区间[min,max]上,如果有数不可以被arr某个子集相加得到,那么其中最小的那个数是arr的最小不可组成和。
3、在区间[min,max]上,如果所有的数都可以被arr的某个子集相加得到,那么max+1是arr的最小不可组成和。
请写函数返回正数数组arr的最小不可组成和。
进阶题目:如果已知正数数组中肯定有1这个数,能否更快得到最小不可组成和?

代码

#include<iostream>
#include<algorithm>
#include<set>
using namespace std;

void process(int* arr, int len, int i, int sum, set<int> &s)
{
	if (i == len)
	{
		s.insert(sum);
		return;
	}
	process(arr, len, i + 1, sum, s);
	process(arr, len, i + 1, sum + arr[i], s);
}

int unformedSum1(int* arr, int len)
{
	if (arr == NULL || len < 1)
		return 1;
	set<int> s;
	process(arr, len, 0, 0, s);
	int Min = INT_MAX;
	int Max = INT_MIN;
	for (int i = 0; i < len; i++)
		Min = min(Min, arr[i]);
	set<int>::iterator it;
	for (it = s.begin(); it != s.end(); it++)
		Max = max(Max, *it);
	for (int i = Min + 1; i <= Max; i++)
		if (s.find(i) == s.end())
			return i;
	return Max + 1;
}

int unformedSum2(int* arr, int len)
{
	if (arr == NULL || len < 1)
		return 1;
	int sum = 0;
	int Min = INT_MAX;
	for (int i = 0; i < len; i++) {
		sum += arr[i];
		Min = min(Min, arr[i]);
	}
	bool* dp = new bool[sum + 1];
	for (int i = 0; i <= sum; i++)
		dp[i] = false;
	dp[0] = true;
	for (int i = 0; i < len; i++)
	{
		for (int j = sum; j >= arr[i]; j--)
			dp[j] = dp[j - arr[i]] ? true : dp[j];
	}
	for (int i = Min; i < sum + 1; i++)
	{
		if (!dp[i])
			return i;
	}
	return sum + 1;
}

int unformedSum3(int* arr, int len)
{
	if (arr == NULL || len < 1)
		return 0;
	sort(arr, arr + len);
	int range = 0;
	for (int i = 0; i < len; i++)
	{
		if (arr[i] > range + 1)
			return range + 1;
		else
			range += arr[i];
	}
	return range + 1;
}

int main()
{
	int len;
	cin >> len;
	int* arr = new int[len];
	for (int i = 0; i < len; i++)
		cin >> arr[i];
	int res1 = unformedSum1(arr, len);
	int res2 = unformedSum2(arr, len);
	int res3 = unformedSum3(arr, len);
	cout << res1 << " " << res2 << " " << res3 << endl;
	getchar();
	return 0;
}

一种字符串和数字的对应关系

题目

一个char类型的数组chs,其中所有的字符都不同。
例如,chs=[‘A’,‘B’,‘C’,…‘Z’],则字符串与整数的对应关系如下:
A,B…Z,AA,AB…AZ,BA,BB…ZZ,AAA…ZZZ,AAAA…
1,2…26,27,28…52,53,54…702,703…18278,18279…
例如,chs=[‘A’,‘B’,‘C’],则字符串与整数对应关系如下:
A,B,C,AA,AB…CC,AAA…CCC,AAAA…
1,2,3, 4, 5… 12, 13… 39, 40…
给定一个数组chs,实现根据对应关系完成字符串与整数相互转换的两个函数。

代码

#include<iostream>
#include<vector>
#include<string>
using namespace std;

char getKthCharAtChs(char* chs, int len, int k)
{
	if (k < 1 || k > len)
		return 0;
	return chs[k - 1];
}

string getString(char* chs, int len, int n)
{
	if (chs == NULL || len < 1 || n < 1)
		return "";
	int cur = 1;
	int base = len;
	int l = 0;
	while (n > cur)
	{
		l++;
		n -= cur;
		cur *= base;
	}
	char* res = new char[l + 1];
	int index = 0;
	int nCur = 0;
	do {
		cur /= base;
		nCur = n / cur;
		res[index++] = getKthCharAtChs(chs, len, nCur + 1);
		n %= cur;
	} while (index != l);
	string sRes;
	for (int i = 0; i < l; i++)
		sRes += res[i];
	return sRes;
}

int getNthFromChar(char* chs, char ch)
{
	int res = -1;
	int len = strlen(chs);
	for (int i = 0; i != len; i++)
	{
		if (chs[i] == ch)
		{
			res = i + 1;
			break;
		}
	}
	return res;
}

int getNum(char* chs, int cLen, string str)
{
	if (chs == NULL || strlen(chs) == 0)
		return 0;
	int len = str.size();
	char* strc = new char[len + 1];
	strcpy(strc, str.c_str());
	strc[len] = '\0';
	int base = cLen;
	int cur = 1;
	int res = 0;
	for (int i = len - 1; i >= 0; i--)
	{
		res += getNthFromChar(chs, strc[i]) * cur;
		cur *= base;
	}
	return res;
}

int main()
{
	int len, val;
	cin >> len >> val;
	char* input = new char[len + 1];
	for (int i = 0; i < len; i++)
		cin >> input[i];
	input[len] = '\0';  //important
	string res = getString(input, len, val);
	int res1 = getNum(input, len, res);
	cout << res << endl;
	cout << res1 << endl;
	getchar();
	return 0;
}

1到n中1出现的次数

题目

给定一个整数n,返回从1到n的数字中1出现的个数。
例如:n=11, 1~n为1,2,3,4,5,6,7,8,9,10,11.那么1出现的次数为4,返回4
剑指offer中有出现一样的题目,最优解是相同的

代码

#include<iostream>
#include<algorithm>
using namespace std;

int get1Nums(int num)
{
	int res = 0;
	while (num != 0)
	{
		if (num % 10 == 1)
			res++;
		num /= 10;
	}
	return res;
}

int sol1(int num)
{
	if (num < 1)
		return 0;
	int cnt = 0;
	for (int i = 1; i <= num; i++)
		cnt += get1Nums(i);
	return cnt;
}

int powBase10(int base)
{
	return pow(10, base);
}

int getLenOfNum(int num)
{
	int len = 0;
	while (num != 0)
	{
		len++;
		num /= 10;
	}
	return len;
}

int sol2(int num)
{
	if (num < 1)
		return 0;
	int len = getLenOfNum(num);
	if (len == 1)
		return 1;
	int tmp = powBase10(len - 1);
	int first = num / tmp;
	int firstOneNum = first == 1 ? num % tmp + 1 : tmp;
	int otherOneNum = first * (len - 1) * (tmp / 10);
	return firstOneNum + otherOneNum + sol2(num % tmp);
}

int main()
{
	int num;
	cin >> num;
	int res1 = sol1(num);
	int res2 = sol2(num);
	cout << res1 << " " << res2 << endl;
	getchar();
	return 0;
}

从N个数中等概率打印M个数和判断一个数字是否为回文数

题目

1、给定一个长度为N且没有重复元素的数组arr和一个整数N,实现函数等概率随机打印arr中的M个数。
要求:相同的数不重复打印;时间复杂度为O(M),额外空间复杂度为O(1);可以改变arr数组;
2、定义回文数概念如下:
如果一个非负数左右完全对应,则该数是回文数,如121,22等;
如果一个负数的绝对值左右完全对应,也是回文数,如-121,-22等;
给定一个32位整数num,判断num是否为回文数

代码

#include<iostream>
#include<algorithm>
using namespace std;
/*每次将打印的数保存于数组的最后一个位置,将范围缩小*/
void swap(int* &arr, int index1, int index2)
{
	int tmp = arr[index1];
	arr[index1] = arr[index2];
	arr[index2] = tmp;
}

void printRandM(int* arr, int len, int m)
{
	if (arr == NULL || len < 1 || m < 0)
		return;
	m = min(len, m);
	int cnt = 0;
	int i = 0;
	while (cnt < m)
	{
		i = (int)(rand() % (len - cnt));
		cout << arr[i] << " ";
		swap(arr, len - cnt - 1, i);  //注意缩减数组大小后数组位置
		cnt++;
	}
	cout << endl;
}

/*判断数组是否为回文数*/
bool isPalindrome(int n)
{
	if (n == INT_MIN)
		return false;
	n = abs(n);
	int help = 1;
	while (n / help >= 10)
		help *= 10;
	while (n != 0)
	{
		if (n / help != n % help)
			return false;
		n = (n % help) / 10;
		help /= 100;
	}
	return true;
}

int main()
{
	int len, m;
	cin >> len >> m;
	int* input = new int[len];
	for (int i = 0; i < len; i++)
		cin >> input[i];
	printRandM(input, len, m);
	getchar();
	return 0;
}

在有序旋转数组中找到最小值以及在有序旋转数组中找到一个数

题目

有序数组arr可能经过一次旋转处理,也可能没有,且arr可能存在重复的数。例如,有序数组[1,2,3,4,5,6,7]可以旋转处理成[4,5,6,7,1,2,3]等。给定一个可能旋转过的有序数组arr,返回arr中的最小值。
给定一个可能旋转过的有序数组arr,再给定一个数num,返回arr中是否含有num。

代码

#include<iostream>
#include<algorithm>
using namespace std;

/*在有序旋转数组中找到最小值(旋转数组中可能有重复值)*/
int findMinValue(int* arr, int len)
{
	if (arr == NULL || len < 1)
		return -1;
	int low = 0;
	int high = len - 1;
	int mid = 0;
	while (low < high)
	{
		if (low == high - 1)
			break;
		if (arr[low] < arr[high])
			return arr[low];
		mid = (low + high) / 2;
		if (arr[low] > arr[mid])
		{
			high = mid;
			continue;
		}
		if (arr[mid] > arr[high])
		{
			low = mid;
			continue;
		}
		while (low < mid)
		{
			if (arr[low] == arr[mid])
				low++;
			else if (arr[low] < arr[mid])
				return arr[low];
			else
			{
				high = mid;
				break;
			}
		}
	}
	return min(arr[low], arr[high]);
}

//在有序旋转数组中找到一个数
bool isContains(int* arr, int len, int num)
{
	int low = 0;
	int high = len - 1;
	int mid = 0;
	while (low <= high)
	{
		mid = (low + high) / 2;
		if (arr[mid] == num)
			return true;
		if (arr[low] == arr[mid] && arr[mid] == arr[high])
		{
			while (low != mid && arr[low] == arr[mid])
				low++;
			if (low == mid)
			{
				low = mid + 1;
				continue;
			}
		}
		if (arr[low] != arr[mid])
		{
			if (arr[mid] > arr[low])
			{
				if (num >= arr[low] && num < arr[mid])
					high = mid - 1;
				else
					low = mid + 1;
			}
			else
			{
				if (num > arr[mid] && num <= arr[high])
					low = mid + 1;
				else
					high = mid - 1;
			}
		}
		else
		{
			if (arr[mid] < arr[high])
			{
				if (num > arr[mid] && num <= arr[high])
					low = mid + 1;
				else
					high = mid - 1;
			}
			else
			{
				if (num >= arr[low] && num < arr[mid])
					high = mid - 1;
				else
					low = mid + 1;
			}  //分析没有看太懂,还是不太清楚为什么多了这么几次
		}
	}
	return false;
}

int main()
{
	int len, num;
	cin >> len >> num;
	int* input = new int[len];
	for (int i = 0; i < len; i++)
		cin >> input[i];
	int minVal = findMinValue(input, len);
	bool res = isContains(input, len, num);
	cout << minVal << endl;
	if (res)
		cout << "contains!" << endl;
	else
		cout << "not contain!" << endl;
	getchar();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值