剑指offer——算法题及解题思路

二维数组中的查找

题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样一个二维数组和一个整数,判断数组中是否含有该整数。

解题思路:矩阵是有序的,从左下角来看,向上数字是递减的,向右数字是递增的,因此从左下角开始查找,当要查找的数字比左下角大时,右移一步;当要查找的数字小时,向上移动一步。

/*
	@param: arr二位数组,target要查找的数字
	@return: 当二位数组中存在要找查找的数字,则返回 1,否则返回 0 
*/
int find(int arr[][N], int target)
{
	int i = N - 1;	//从左下角开始
	int j = 0;
	while(i >= 0 && j < N)
	{
		if(target == arr[i][j])	//找到要查找的数字
			return 1;
		if(target > arr[i][j])	//比左下角的数字大,右移一步
			j++;
		else	//比左下角的数字小,向上移动一步
			i--;
	}
	return 0;
}

不用加减乘除做加法

题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/ 四则运算符号。

解题思路:用三步走的方式计算二进制值相加:

  1. 相加各位的值,不算进位。二进制每位相加就相当于各位做异或(^)操作。
  2. 计算进位值。相当于各位做与(&)操作,再左移一位。
  3. 重复上述两步,直到进位为0,则跳出循环。

exp:5(101)7(0111)

  1. 101^111 == 010
  2. (101&111)<<1 == 1010
  3. 重复上面两步,0010 ^1010 == 1000, (0010&1010)<<1 ==0100.
    再重复,1000^0100 == 1100, (1000&0100)<<1 ==0,跳出循环,返还1100(12)。
int Add(int num1, int num2)
{
	int sum, carry;
	while(0 != num2)
	{
		//对应位相加但不进位,用异或实现
		sum = num1^num2;
		//记下进位,用与 和 移位实现
		carry = (num1&num2)<<1;
		num1 = sum;
		num2 = carry;
	}
	return num1;
}

替换空格

题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为 I Am A Handsome Boy. 则经过替换之后的字符串为I%20Am%20A%20Handsome%20Boy.

解题思路:从前往后替换,后面的字符要不断地往后移动,要多次移动,所以效率低下。从后往前移动,先计算需要多少空间,然后从后往前移动,则每个字符只会移动一次,这样效率更高一点。

/*
	@param: str字符串
*/
void replaceSpace(char *str)
{
	int Oldlen = 0;	//记录字符串原来的长度
	int cnt = 0;	//记录空格的数量
	int Newlen;	//记录插入后的长度
	while(str[Oldlen] != '\0')	//遍历一遍字符串找出空格的数量
	{	
		if(str[Oldlen] == ' ')
			cnt++;
		Oldlen++;
	}
	Newlen = Oldlen + 2*cnt;	//插入后的长度,不用减一因为隐藏个'\0'也要算上
	if(Newlen > max_size)	//如果计算后的长度大于总长度就无法插入
		returnwhile(Oldlen >= 0 && Newlen > Oldlen)	//放字符
	{
		if(str[Oldlen] == ' ')	//碰到空格就替换
		{
			str[Newlen--] = '0';
			str[Newlen--] = '2';
			str[Newlen--] = '%';
		}
		else	//不是空格就将Oldlen指向的字符放入Newlen指向的位置
			str[Newlen--] = str[Oldlen];
		Oldlen--;	//不管是if还是else,Oldlen都要前移
	}
}

反转链表

题目描述
输入一个链表,反转链表后,输出新的表头。

解题思路:
在这里插入图片描述

ListNode* ReverseList(ListNode* pHead)
{
	if(NULL == pHead)	//判断链表是否为空
		return NULL;
	ListNode* pre = NULL;
	ListNode* next = NULL;
	while(pHead)
	{
		next = pHead->next;
		pHead->next = pre;
		pre = pHead;
		pHead = next;
	}
	return pre;	//pre成为新的头结点
}

青蛙跳台阶

问题描述
一只青蛙一次可以跳上1一级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。(先后次序不同算不同的结果)

解题思路:跟斐波那契数列一样的解法,没啥好说的。

当n=1时,有1种跳法;
当n=2时,有2种跳法;
当n=3时,有3种跳法;
当n=4时,有5种跳法;
……
当n时;有f(n-1) +f(n-2)种跳法;

int jumpFloor(int number)
{
	if(number <= 2)
		return number;
	int num1 = 1;
	int num2 = 2;
	int tmp;
	int i;
	for(i=2; i<number; i++)
	{
		tmp = num1 +num2;
		num1 = num2;
		num2 = tmp;
	}
	return num2;
}

变态台阶

题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路:
f(1)=1
f(2) = f(2-1) + f(2-2) //f(2-2)表示2阶一次跳2级的次数
f(3) = f(3-1) + f(3-2) + f(3-3)
……
f(n) = f(n-1) + f(n-2) + f(n-3)+……+ f(n-(n-1)) +f(n-n)

说明:

  1. 这里的f(n)代表的是n个台阶有一次1,2,……n级的跳法数。
  2. n = 1时,只有1种的跳法,f(1) = 1
  3. n = 2时,会有两种跳法,一次1级或2级。f(2) = f(0) + f(1)
  4. n = 3时,会有三种跳法,一次1级、2级或3级。那么就是第一次跳出1级后面剩下: f(3-1); 第一次跳出2级,剩下 f(3-2); 第一次跳出3级,剩下 f(3-3)。因此结论为:f(3) = f(3-1) + f(3-2) + f(3-3)
  5. n = n时,会有n种跳法,一次1级、2级…n级。得出结论:f(n) = f(n-1) + f(n-2)+……+f(n-(n-1)) + f(n-n) ---->f(0) +f(1) + f(2) + f(3) + ……+ f(n-2) + f(n-1)。
    可以简化为:f(n) = 2*f(n-1)
  6. 得出结论:
    f(n) : n = 0时 f(n) = 0;f(n) = 1时 f(n) = 1;n>=2时 f(n) = 2*f(n-1).
int jumpFloorII(int number)
{
	if(0 == number || 1 == number)
		return number;
	int i;
	int power = 2;
	for(i=2; i<number; i++)
	{
		power *= 2;
	}
	return power;
}

调整数组顺序使奇数位于偶数前面

题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

示例:

输入:[1 2 3 4 5 6 7]
输出:[1 3 5 7 2 4 6]

解题思路:
类似于冒泡排序,前偶后奇就交换。

void reOrderArray(int *array, int len)
{
	int i, j;
	for(i=0; i<len-1; i++)
	{
		for(j=len-1; j>i; j--)
		{
			//前偶后奇,则交换
			if(!(array[j-1]&1) && array[j]&1)
			{
				array[j] = array[j]^array[j-1];
				array[j-1] = array[j]^array[j-1];
				array[j] = array[j]^array[j-1];
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值