You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class20;
import java.util.Arrays;
public class Code03_ShuffleProblem {
// 数组的长度为len调整前的位置是i返回调整之后的位置
// 下标不从0开始从1开始
public static int modifyIndex1(int i, int len) {
if (i <= len / 2) {
return 2 * i;
} else {
return 2 * (i - (len / 2)) - 1;
}
}
// 数组的长度为len调整前的位置是i返回调整之后的位置
// 下标不从0开始从1开始
public static int modifyIndex2(int i, int len) {
return (2 * i) % (len + 1);
}
// 主函数
// 数组必须不为空,且长度为偶数
public static void shuffle(int[] arr) {
if (arr != null && arr.length != 0 && (arr.length & 1) == 0) {
shuffle(arr, 0, arr.length - 1);
}
}
// 在arr[L..R]上做完美洗牌的调整arr[L..R]范围上一定要是偶数个数字)
public static void shuffle(int[] arr, int L, int R) {
while (R - L + 1 > 0) { // 切成一块一块的解决,每一块的长度满足(3^k)-1
int len = R - L + 1;
int base = 3;
int k = 1;
// 计算小于等于len并且是离len最近的满足(3^k)-1的数
// 也就是找到最大的k满足3^k <= len+1
while (base <= (len + 1) / 3) { // base > (N+1)/3
base *= 3;
k++;
}
// 3^k -1
// 当前要解决长度为base-1的块一半就是再除2
int half = (base - 1) / 2;
// [L..R]的中点位置
int mid = (L + R) / 2;
// 要旋转的左部分为[L+half...mid], 右部分为arr[mid+1..mid+half]
// 注意在这里arr下标是从0开始的
rotate(arr, L + half, mid, mid + half);
// 旋转完成后从L开始算起长度为base-1的部分进行下标连续推
cycles(arr, L, base - 1, k);
// 解决了前base-1的部分剩下的部分继续处理
L = L + base - 1; // L -> [] [+1...R]
}
}
// 从start位置开始往右len的长度这一段做下标连续推
// 出发位置依次为1,3,9...
public static void cycles(int[] arr, int start, int len, int k) {
// 找到每一个出发位置trigger一共k个
// 每一个trigger都进行下标连续推
// 出发位置是从1开始算的而数组下标是从0开始算的。
for (int i = 0, trigger = 1; i < k; i++, trigger *= 3) {
int preValue = arr[trigger + start - 1];
int cur = modifyIndex2(trigger, len);
while (cur != trigger) {
int tmp = arr[cur + start - 1];
arr[cur + start - 1] = preValue;
preValue = tmp;
cur = modifyIndex2(cur, len);
}
arr[cur + start - 1] = preValue;
}
}
// [L..M]为左部分,[M+1..R]为右部分,左右两部分互换
public static void rotate(int[] arr, int L, int M, int R) {
reverse(arr, L, M);
reverse(arr, M + 1, R);
reverse(arr, L, R);
}
// [L..R]做逆序调整
public static void reverse(int[] arr, int L, int R) {
while (L < R) {
int tmp = arr[L];
arr[L++] = arr[R];
arr[R--] = tmp;
}
}
public static void wiggleSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
// 假设这个排序是额外空间复杂度O(1)的,当然系统提供的排序并不是,你可以自己实现一个堆排序
Arrays.sort(arr);
if ((arr.length & 1) == 1) {
shuffle(arr, 1, arr.length - 1);
} else {
shuffle(arr, 0, arr.length - 1);
for (int i = 0; i < arr.length; i += 2) {
int tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
}
}
}
// for test
public static boolean isValidWiggle(int[] arr) {
for (int i = 1; i < arr.length; i++) {
if ((i & 1) == 1 && arr[i] < arr[i - 1]) {
return false;
}
if ((i & 1) == 0 && arr[i] > arr[i - 1]) {
return false;
}
}
return true;
}
// for test
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// for test
public static int[] generateArray() {
int len = (int) (Math.random() * 10) * 2;
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * 100);
}
return arr;
}
public static void main(String[] args) {
for (int i = 0; i < 5000000; i++) {
int[] arr = generateArray();
wiggleSort(arr);
if (!isValidWiggle(arr)) {
System.out.println("ooops!");
printArray(arr);
break;
}
}
}
}