1、数字在排序数组中出现的次数(剑指offer--38)
题目:统计一个数字在排序数组中出现的次数。例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4.
3、和为S的两个数字VS和为s的连续正数序列(剑指offer--41)
题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可。
例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。
思路:最初我们找到数组的第一个数字和最后一个数字。首先定义两个指针,第一个指针指向数组的第一个(也就是最小的)数字,第二个指针指向数组的最后一个(也就是最大的)数字。当两个数字的和大于输入的数字时,把较大的数字往前移动;当两个数字的和小于数字时,把较小的数字往后移动;当相等时,打完收工。这样扫描的顺序是从数组的两端向数组的中间扫描。
有了解决前面问题的经验,这里也考虑两个数small和big分别表示序列的最小值和最大值。
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。
步骤:
先对每一部分旋转,abcdefg 变成bagfedc,之后,对整个字符串进行翻转。bagfedc - cdefgab
题目:统计一个数字在排序数组中出现的次数。例如输入排序数组{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);
}