算法练习(排序与检索)
作者:互联网
1.
private class largestNumberComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
String s1 = o1 + o2;
String s2 = o2 + o1;
return s2.compareTo(s1);
}
}
public String largestNumber(int[] nums) {
int n = nums.length;
if (nums.length == 1) {
return String.valueOf(nums[0]);
}
String[] strings = new String[n];
for (int i = 0; i < n; i++) {
strings[i] = String.valueOf(nums[i]);
}
Arrays.sort(strings, new largestNumberComparator());
if (strings[0].equals("0")) {
return "0";
}
String str = new String();
for (String string : strings) {
str += string;
}
return str;
}
2.
/*数组按照从小到大排序后,从中间切分,比如 123456 切分后123,456 穿插进行后142536符合题意
但是1223这种就不行了,但是穿插规则可以变一下,两部分逆序穿插,即2 3 1 2*/
public void wiggleSort(int[] nums) {
int[] temp = nums.clone();
Arrays.sort(temp);
int n = nums.length;
for (int i = 1; i < nums.length; i += 2) {
nums[i] = temp[--n];
}
for (int i = 0; i < nums.length; i += 2) {
nums[i] = temp[--n];
}
}
3.
public int findPeakElement(int[] nums) {
if (nums.length == 1){
return 0;
}
for (int i = 0; i < nums.length; i++) {
if (i == 0) {
if (nums[i] > nums[i + 1]) {
return i;
}
} else if (i == nums.length - 1) {
if (nums[i] > nums[i - 1]) {
return i;
}
} else {
if (nums[i] > nums[i + 1] && nums[i] > nums[i - 1]) {
return i;
}
}
}
return 0;
}
public int findPeakElement(int[] nums) {
//迭代法,O(log2(n))
int = 0, r = nums.length - 1;
while (l < r) {
int mid = (l + r) / 2;
if (nums[mid] > nums[mid + 1])
r = mid;
else
l = mid + 1;
}
return l;
}
4.
public int findDuplicate(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for (int num : nums) {
if (set.contains(num)){
return num;
}else {
set.add(num);
}
}
return Integer.MAX_VALUE;
}
public int findDuplicate(int[] nums) {
//快慢指针法
int slow = 0, fast = 0;
do {
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);
slow = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
5.
/*假设我们有两个已排序的序列等待合并,分别是 L={8,12,16,22,100} 和 R={7,26,55,64,91}。一开始我们用指针 lPtr = 0 指向 LL 的头部,rPtr = 0 指向 RR 的头部。记已经合并好的部分为 MM。
L = [8, 12, 16, 22, 100] R = [7, 26, 55, 64, 91] M = []
| |
lPtr rPtr
我们发现 lPtr 指向的元素大于 rPtr 指向的元素,于是把 rPtr 指向的元素放入答案,并把 rPtr 后移一位。
L = [8, 12, 16, 22, 100] R = [7, 26, 55, 64, 91] M = [7]
| |
lPtr rPtr
接着我们继续合并:
L = [8, 12, 16, 22, 100] R = [7, 26, 55, 64, 91] M = [8, 9]
| |
lPtr rPtr
此时 lPtr 比 rPtr 小,把 lPtr 对应的数加入答案。如果我们要统计 88 的右边比 88 小的元素,这里 77 对它做了一次贡献。如果带合并的序列 L = \{ 8, 12, 16, 22, 100 \}L={8,12,16,22,100},R = \{ 7, 7, 7, 26, 55, 64, 91\}R={7,7,7,26,55,64,91},那么一定有一个时刻,lPtr 和 rPtr 分别指向这些对应的位置:
L = [8, 12, 16, 22, 100] R = [7, 7, 7, 26, 55, 64, 91] M = [7, 7, 7]
| |
lPtr rPtr
下一步我们就是把 88 加入 MM 中,此时三个 77 对 88 的右边比 88 小的元素的贡献为 33。以此类推,我们可以一边合并一边计算 RR 的头部到 rPtr 前一个数字对当前 lPtr 指向的数字的贡献。
我们发现用这种「算贡献」的思想在合并的过程中计算逆序对的数量的时候,只在 lPtr 右移的时候计算,是基于这样的事实:当前 lPtr 指向的数字比 rPtr 小,但是比 RR 中 [0 ... rPtr - 1] 的其他数字大,[0 ... rPtr - 1] 的数字是在 lPtr 右边但是比 lPtr 对应数小的数字,贡献为这些数字的个数。
但是我们又遇到了新的问题,在「并」的过程中 88 的位置一直在发生改变,我们应该把计算的贡献保存到哪里呢?这个时候我们引入一个新的数组,来记录每个数字对应的原数组中的下标,例如:
a = [8, 9, 1, 5, 2]
index = [0, 1, 2, 3, 4]
排序的时候原数组和这个下标数组同时变化,则排序后我们得到这样的两个数组:
a = [1, 2, 5, 8, 9]
index = [2, 4, 3, 0, 1]
我们用一个数组 ans 来记录贡献。我们对某个元素计算贡献的时候,如果它对应的下标为 p,我们只需要在 ans[p] 上加上贡献即可。*/
private int[] index;
private int[] temp;
private int[] tempIndex;
private int[] ans;
public List<Integer> countSmaller(int[] nums) {
this.index = new int[nums.length];
this.temp = new int[nums.length];
this.tempIndex = new int[nums.length];
this.ans = new int[nums.length];
for (int i = 0; i < nums.length; ++i) {
index[i] = i;
}
int l = 0, r = nums.length - 1;
mergeSort(nums, l, r);
List<Integer> list = new ArrayList<Integer>();
for (int num : ans) {
list.add(num);
}
return list;
}
public void mergeSort(int[] a, int l, int r) {
if (l >= r) {
return;
}
int mid = (l + r) >> 1;
mergeSort(a, l, mid);
mergeSort(a, mid + 1, r);
merge(a, l, mid, r);
}
public void merge(int[] a, int l, int mid, int r) {
int i = l, j = mid + 1, p = l;
while (i <= mid && j <= r) {
if (a[i] <= a[j]) {
temp[p] = a[i];
tempIndex[p] = index[i];
ans[index[i]] += (j - mid - 1);
++i;
++p;
} else {
temp[p] = a[j];
tempIndex[p] = index[j];
++j;
++p;
}
}
while (i <= mid) {
temp[p] = a[i];
tempIndex[p] = index[i];
ans[index[i]] += (j - mid - 1);
++i;
++p;
}
while (j <= r) {
temp[p] = a[j];
tempIndex[p] = index[j];
++j;
++p;
}
for (int k = l; k <= r; ++k) {
index[k] = tempIndex[k];
a[k] = temp[k];
}
}
标签:检索,return,nums,int,length,lPtr,算法,rPtr,排序 来源: https://blog.csdn.net/cy1798/article/details/114481282