数据结构---数组(5)

1、数字在排序数组中出现的次数(剑指offer--38)
题目:统计一个数字在排序数组中出现的次数。例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4.

思路:用二分查找,分别找出第一个3,和最后一个3的位置,然后计算个数。

	public static int getLower(int arr[], int key) {
		int low = 0, high = arr.length - 1;

		while (low <= high) {
			int mid = (low + high) / 2;
			if (arr[mid] < key) {
				low = mid + 1;
			} else if (arr[mid] > key) {
				high = mid - 1;
			} else {
				if ((mid > 0 && arr[mid - 1] != key) || mid == 0) {
					return mid;
				} else {
					high = mid - 1;
				}
			}
		}

		if (low > high)
			return -1;
		return low;
	}

	public static int getUpper(int arr[], int key) {
		int low = 0, high = arr.length - 1;

		while (low <= high) {
			int mid = (low + high) / 2;
			if (arr[mid] < key) {
				low = mid + 1;
			} else if (arr[mid] > key) {
				high = mid - 1;
			} else {
				if ((mid < arr.length - 1 && arr[mid + 1] != key)
						|| mid == arr.length - 1) {
					return mid;
				} else {
					low = mid + 1;
				}

			}
		}

		if (low > high)
			return -1;
		return low;
	}

	public static int getNumberOfK(int arr[], int key) {
		if (arr == null)
			return -1;
		int number = 0;
		int first = getLower(arr, key);
		int last = getUpper(arr, key);
		if (first > -1 && last > -1)
			number = last - first + 1;
		return number;
	}

2、数组中只出现一次的数(剑指offer--40)

题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
通过这道题感觉位运算很强大,这道题利用异或的几个性质:任何数与其本身异或值都为0,异或运算满足交换律。因此将一组数依次异或,若里面只有一个只出现一次的数,其他的数都出现两次,则最后的结果必然是那个只出现一次的数。要找到两个数字就可以先通过异或整个数组,将得到的结果分组。然后依次安组异或就可以得到所求的值。

	public static String findNums(int date[], int length) {
		if (length < 2) {
			return "|";
		}
		int ansXor = 0;
		for (int i = 0; i < length; i++) {
			ansXor ^= date[i]; // 异或
		}

		int pos = findFirstOne(ansXor);
		int num1 =0, num2 = 0;
		for (int i = 0; i < length; i++) {
			if (testBit(date[i], pos-1) == 1)
				num1 ^= date[i];
			else
				num2 ^= date[i];
		}
		return num1 + "|" + num2;
	}

	public static int findFirstOne(int value) { // 取二进制中首个为1的位置
		int pos = 1;
		while ((value & 1) != 1) {
			value = value >> 1;
			pos++;
		}
		return pos;
	}

	public static int testBit(int value, int pos) { // 测试某位置是否为1
		return ((value >> pos) & 1);
	}

3、和为S的两个数字VS和为s的连续正数序列(剑指offer--41)
题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可。
例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。
思路:最初我们找到数组的第一个数字和最后一个数字。首先定义两个指针,第一个指针指向数组的第一个(也就是最小的)数字,第二个指针指向数组的最后一个(也就是最大的)数字。当两个数字的和大于输入的数字时,把较大的数字往前移动;当两个数字的和小于数字时,把较小的数字往后移动;当相等时,打完收工。这样扫描的顺序是从数组的两端向数组的中间扫描。

	public static String FindNumbersWithSum(int data[], int length, int sum) {
		if (length < 1 || data == null)
			return "";

		int end = length - 1;
		int start = 0;
		int curSum = 0;

		while (start < end) {
			curSum = data[start] + data[end];
			if (curSum == sum) {
				return data[start] + "+" + data[end] + "=" + curSum;

			} else if (curSum > sum)
				end--;
			else
				start++;
		}
		return "";
	}
题目:输入一个正数S,打印出所有和为S的连续正数序列(至少有两个数)。例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以结果打印出3个连续序列1~5,4~6和7~8.
有了解决前面问题的经验,这里也考虑两个数small和big分别表示序列的最小值和最大值。

首先把small初始化为1,big初始化为2.

如果从small到big的序列的和大于S,可以从序列中去掉较小的值,也就是增大small的值。

如果从small到big的序列的和小于S,可以增大big,让这个序列包含更多的数字。因为这个序列至少要有两个数字,我们一直增加small到(1+S)/2为止。

如果从small到big的序列的和等于S,输出序列,之后增大big。

	public static void FindContinuousSequence(int sum) {
		if (sum < 3)
			return;

		int small = 1;
		int big = 2;
		int middle = (1 + sum) / 2;
		int curSum = small + big;

		while (small < middle) {
			if (curSum == sum) {
				String print = small + "";
				for (int i = small + 1; i <= big; i++) {
					print += "+" + i;
				}
				System.out.println(print+"="+curSum);
				big++;
				curSum +=big;
			} else if (curSum < sum) {
				big++;
				curSum +=big;
			} else {
				curSum -=small;
				small++;
			}
		}
	}
4、翻转句子中单词的顺序vs左旋字符串(剑指offer--42)
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。

分析:由于本题需要翻转句子,我们先颠倒句子中的所有字符。这时,不但翻转了句子中单词的顺序,而且单词内字符也被翻转了。

我们再颠倒每个单词内的字符。由于单词内的字符被翻转两次,因此顺序仍然和输入时的顺序保持一致。

还是以上面的输入为例子。翻转“I am a student.”中所有字符得到“.tneduts a ma I”,再翻转每个单词中字符的顺序得到“students. a am I”,正是符合要求的输出。

	public static void reverse(char[] str, int start, int end) {
		if (str == null || start < 0 || end < 0)
			return;
		char temp;

		while (start < end) {
			temp = str[start];
			str[start] = str[end];
			str[end] = temp;

			start++;
			end--;
		}
	}

	public static void reverseSentence(char[] str) {
		if (str == null)
			return;

		// 翻转整个数组
		int len = str.length;
		reverse(str, 0, len - 1);

		int start = 0;
		int end = 0;

		// 翻转每个单词
		while (end < len) {
			if (end == len - 1) {
				reverse(str, start, end);
			}
			if (str[end] == ' ') {
				reverse(str, start, end - 1);
				end++;
				start = end;
			} else {
				end++;
			}
		}
	} 
题目:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。 如把字符串abcdef左旋转2位得到字符串cdefab。
步骤:
先对每一部分旋转,abcdefg 变成bagfedc,之后,对整个字符串进行翻转。bagfedc - cdefgab

	public static void reverse(char[] str, int start, int end) {
		if (str == null || start < 0 || end < 0)
			return;
		char temp;

		while (start < end) {
			temp = str[start];
			str[start] = str[end];
			str[end] = temp;

			start++;
			end--;
		}
	}

	// 左旋字符串(翻转前count字符)
	public static void reverseLeftSentence(char[] str, int count) {
		if (str == null || count < 1 || count > str.length)
			return;

		int start = 0;
		int end = str.length - 1;

		// 翻转单词
		reverse(str, start, count - 1);
		reverse(str, count, end);

		// 翻转句子
		reverse(str, start, end);
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值