1. 求解满足条件的元素对个数问题
【问题描述】 给定N个整数Ai以及一个正整数C,问其中有多少对i、j满足Ai-Aj=C。
【输入描述】第1行输入两个空格隔开的整数N和C,第2~N+1行每行包含一个整数Ai。
【输出描述】输出一个数表示答案。
【输入样例】5 3
2
1
4
2
5
【输出样例】3
public class Solution1 {
/**
* 解法一:先排序,然后双指针遍历
* 时间复杂度O(logn)+O(n^2)
*/
public int countElementPairs(int[] nums, int C) {
int len = nums.length;
int cnt = 0;
Arrays.sort(nums);
System.out.println(Arrays.toString(nums));
for (int i = 0; i < len; i++) {
for (int j = i + 1; j < len; j++) {
if (nums[j] - nums[i] == C) {
cnt++;
} else if (nums[j] - nums[i] > C) {
break;
}
}
}
return cnt;
}
/**
* 解法二:先排序,然后用二分法找nums[i]+C
* 时间复杂度O(logn)+O(nlogn)
*/
public int countElementPairs2(int[] nums, int C) {
int len = nums.length;
int cnt = 0;
Arrays.sort(nums);
for (int i = 0; i < len; i++) {
cnt += countTarget(nums, nums[i] + C);
}
return cnt;
}
// 统计有序数组中target的个数
private int countTarget(int[] nums, int target) {
int borderL = serchLeftBorder(nums, target);
int borderR = serchRightBorder(nums, target);
if (borderL != -1 && borderR != -1) {
return borderR - borderL + 1;
} else {
return 0;
}
}
// 找有序数组中target的最左下标
private int serchLeftBorder(int[] nums, int target) {
int len = nums.length;
int left = 0;
int right = len - 1;
int mid = 0;
while (left <= right) {
mid = (left + right) / 2;
if (nums[mid] == target) {
if (mid == 0 || nums[mid - 1] != target) {
return mid;
} else {
right = mid - 1;
}
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
// 找有序数组中target的最右下标
private int serchRightBorder(int[] nums, int target) {
int len = nums.length;
int left = 0;
int right = len - 1;
int mid = 0;
while (left <= right) {
mid = (left + right) / 2;
if (nums[mid] == target) {
if (mid == len - 1 || nums[mid + 1] != target) {
return mid;
} else {
left = mid + 1;
}
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int C = scanner.nextInt();
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = scanner.nextInt();
}
System.out.println(new Solution1().countElementPairs2(nums, C));
}
}
2. 求解查找最后一个小于等于指定的元素问题
【问题描述】 给一个长度为n的单调递增的正整数序列,即序列中每一个数都比前一个数大。有m个询问,每次询问一个x,问序列中最后一个小于等于x的数是什么?
【输入描述】第一行两个整数n,m。接下来一行n个数,表示这个序列。接下来m行每行一个数,表示一个询问。
【输出描述】输出共m行,表示序列中最后一个小于等于x的数是什么。假如没有,则输出-1。
【输入样例】5 3
1 2 3 4 6
5
1
3
【输出样例】4
1
3
public class Solution2 {
/**
* 本题与Java的Arrays.binarySearch的源码实现,有异曲同工之妙。
* 只要我们能理解为什么二分查找是安全的,那么理解下面的代码就会非常简单。
*
* 那么问自己一句,二分查找安全性的关键在于何处?
* 在于无论什么时候,num[min]<=target,num[max]>=target。
*
* 在一个递增(不重复)数组中,二分查找一个不存在的数,必然会打破上面的安全性,即出现一次"反常"———num[min]>target,num[max]<target
* 当出现这次反常后,之后的二分便不再具有意义
* 同时我们是可以保证的,目标值(target)就出现在第一次反常之处:
* if(num[min]>target)即左侧反常,target的值最接近于 nums[left - 1] 或者 nums[left]
* if(num[max]<target)即右侧反常,target的值最接近于 nums[right] 或者 nums[right + 1]
*
*
* 回到本题:
* 本题需要取左,因此不管target靠近谁,一律取小值: nums[left - 1] 或者 nums[right]
*
* 给两个极其极其好的例子:
* [1, 3, 4, 6] search 5
* [1, 3, 4, 6] search 2
*
*/
public int findNum(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int mid = 0;
while (left <= right) {
mid = (left + right) / 2;
if (nums[left] > target) {
if (left - 1 < 0) {
return -1;
}
return nums[left - 1];
}
if (nums[right] < target) {
return nums[right];
}
if (nums[mid] == target) {
return nums[mid];
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
return -1;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 数组的长度
int len1 = scanner.nextInt();
// 输入的长度
int len2 = scanner.nextInt();
int[] nums = new int[len1];
for (int i = 0; i < len1; i++) {
nums[i] = scanner.nextInt();
}
int[] res = new int[len2];
Solution2 solution2 = new Solution2();
for (int i = 0; i < len2; i++) {
res[i] = solution2.findNum(nums, scanner.nextInt());
}
for (int n : res) {
System.out.println(n);
}
}
}
3. 求解递增序列中与x最接近的元素问题
【问题描述】 给一个长度为n的单调递增的正整数序列,即序列中每一个数都比前一个数大。有m个询问,每次询问一个x,问序列中最后一个小于等于x的数是什么?
【输入描述】第一行两个整数n,m。接下来一行n个数,表示这个序列。接下来m行每行一个数,表示一个询问。
【输出描述】输出共m行,表示序列中最后一个小于等于x的数是什么。假如没有,则输出-1。
【输入样例】5 3
1 2 3 4 6
5
1
3
【输出样例】4
1
3
public class Solution3 {
/**
* 关键还是要理解二分法的安全性,以及打破这种安全的"反常"
*
*/
public int findNearest(int[] nums, int target) {
int len = nums.length;
if (nums[0] > target) {
return nums[0];
}
if (nums[len - 1] < target) {
return nums[len - 1];
}
int left = 0;
int right = len - 1;
int mid = 0;
while (left <= right) {
mid = (left + right) / 2;
if (nums[left] > target) {
return target - nums[left - 1] <= nums[left] - target ? nums[left - 1] : nums[left];
}
if (nums[right] < target) {
return target - nums[right] <= nums[right + 1] - target ? nums[right] : nums[right + 1];
}
if (nums[mid] == target) {
return nums[mid];
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
return -1;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 数组的长度
int len1 = scanner.nextInt();
int[] nums = new int[len1];
for (int i = 0; i < len1; i++) {
nums[i] = scanner.nextInt();
}
// 输入的长度
int len2 = scanner.nextInt();
int[] res = new int[len2];
Solution3 solution3 = new Solution3();
for (int i = 0; i < len2; i++) {
res[i] = solution3.findNearest(nums, scanner.nextInt());
}
for (int n : res) {
System.out.println(n);
}
}
}
4. 求解按"最多排序"到"最少排序"的顺序排列问题
【问题描述】 一个序列中的“未排序”的度量是相对于彼此顺序不一致的条目对的数量,例如,在字母序列“DAABEC"中该度量为5,因为D大于其右边的4个字母,E大于其右边的1个字母。该度量称为该序列的逆序数。序列“AACEDGG”只有一个逆序对(E和D),它几乎被排好序了,而序列“ZWQM"有6个逆序对,它是未排序的,恰好是反序。需要对若干个DNA序列(仅包含4个字母A、C、G和T的字符串)分类,注意是分类而不是按字母顺序排列,是按照“最多排序”到“最小排序”的顺序排列,所有DNA序列的长度都相同。
【输入描述】第1行包含两个整数,n(0≤n≤50)表示字符串长度,m(0<m≤100)表示字符串个数;后面是m行,每行包含一个长度为n的字符串。
【输出描述】按“最多排序”到“最小排序”的顺序输出所有字符串。若两个字符串的逆序对个数相同,按原始顺序输出它们。
【输入样例】10 6
AACATGAAGG
TTTTGGCCAA
TTTGGCCAAA
GATCAGATTT
CCCGGGGGGA
ATCGATGCAT
【输出样例】4
CCCGGGGGGA
AACATGAAGG
GATCAGATTT
ATCGATGCAT
TTTTGGCCAA
TTTGGCCAAA
class DNA {
String sequence; // 序列
int cost; // 度量
public DNA(String sequence) {
this.sequence = sequence;
char[] charArray = sequence.toCharArray();
int len = charArray.length;
for (int i = 0; i < len; i++) {
for (int j = i + 1; j < len; j++) {
cost += (charArray[i] - charArray[j] > 0 ? 1 : 0);
}
}
}
@Override
public String toString() {
return sequence;
}
}
public class Solution4 {
public void sortByCost(List<DNA> dnaList) {
// 可以归并排序,但没必要(雾
Collections.sort(dnaList, Comparator.comparingInt(o -> o.cost));
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int dnaLen = scanner.nextInt();
int len = scanner.nextInt();
List<DNA> dnaList = new ArrayList<>();
for (int i = 0; i < len; i++) {
dnaList.add(new DNA(scanner.next()));
}
new Solution4().sortByCost(dnaList);
for (DNA dna : dnaList) {
System.out.println(dna);
}
}
}
⭐️ 这一定是全网最优美的Java解法 >_<