算法筆記:將數組按符號排序

 

題目:html

給定一個int數組,長度爲n,數組中每一個元素爲隨機整數,可能爲負數,可能爲0,可能爲正數,要求將數組按照符號排序,全部的負數在左邊,正數在右邊,零在中間,負數和負數之間不須要有序,正數和正數之間也不須要有序。java

數據約束:數組

0 < n <= 300000000dom

例子:
輸入:
[0, 3, 5, -10, -1]函數

輸出:測試

下面任意一個都是合法輸出:指針

[-10, -1, 0, 3, 5]htm

[-1, -10, 0, 3, 5]blog

[-10, -1, 0, 5, 3]排序

[-1, -10, 0, 5, 3]

 

這道題中的難點是出現了三種類型的數值,不能簡單地採用兩個指針從兩端向中間收縮的遍歷方式,除非使用額外的空間(這個空間還不是臨時的,而是在函數執行完畢仍然可能被佔用的)。

即新申請一個長度爲n的int數組,由於int數組中元素默認值爲0,至關於不用考慮0的狀況,將三種狀況轉爲了兩種狀況,就比較容易處理了,只須要將原數組中的值按照負數放在新數組的左端,正數放在新數組的右端便可,這種方式時間複雜度爲O(n),只須要遍歷一遍數組便可,可是須要額外的至少4n字節的內存空間。

實現代碼以下:

package org.cc11001100.alg.sortBySymbol;

/**
 * 須要額外空間的按符號排序,時間複雜度是O(n),可是須要額外的內存空間n
 *
 * @author CC11001100
 */
public class SortBySymbolSolutionNeedMoreMemory implements SortBySymbol {

	// 拷貝到一個新的數組中
	public int[] sortBySymbol(int[] nums) {
		int[] result = new int[nums.length];
		int left = 0, right = result.length - 1;
		for (int i = 0; i < nums.length; i++) {
			if (nums[i] < 0) {
				result[left++] = nums[i];
			} else if (nums[i] > 0) {
				result[right--] = nums[i];
			}
		}
		return result;
	}

	public static void main(String[] args) {
		SortBySymbol sortBySymbol = new SortBySymbolSolutionNeedMoreMemory();
//		int[] nums = RandomIntArrayGenerator.random(100000000);
//		long start = System.currentTimeMillis();
//		nums = sortBySymbol.sortBySymbol(nums);
//		long cost = System.currentTimeMillis() - start;
//		System.out.println(cost);
//		System.out.println(SortBySymbolJudge.judge(nums));
//		for (int i = 0; i < nums.length; i++) {
//			System.out.println(nums[i]);
//		}
		SortBySymbolJudge.judge(sortBySymbol);
	}

}

上面的代碼只是實現了基本的要求,咱們確定可以作到更好對吧,對於用到了額外空間的解法,咱們應該想一下是否能夠不使用額外空間就解決這個問題呢?

上面說了,這個問題複雜就複雜在要在一個數組中操縱三種類型的數據,指針不夠用,那麼咱們能夠將這個問題轉化一下,好比能夠將問題分爲兩步:

1. 先按照負數和非負數(包括零和正數)進行排序,這一步將負數的順序排好了,可是零和正數仍是無序的。

2. 再按照零和正數排序,排完以後總體有序了。

這樣每一步都是兩種類型的數據,使用兩個指針徹底夠用了,並且不須要使用到額外的內存了,缺點就是須要多遍歷一遍數組,時間複雜度是O(2n),約掉常數複雜度仍然是O(n)。

代碼實現:

package org.cc11001100.alg.sortBySymbol;

/**
 * 不須要額外空間,可是須要掃描兩次數組,時間複雜度是O(2n),可是不須要額外空間
 *
 * @author CC11001100
 */
public class SortBySymbolSolutionTwoTimesSort implements SortBySymbol {

	// 將整個排序過程看作是兩個步驟,先按負數和非負數排序,而後將非負數部分按照零和正數排序
	public int[] sortBySymbol(int[] nums) {
		int left = negativeAndNotNegativeSort(nums);
		zeroAndPositiveSort(nums, left);
		return nums;
	}

	// 負數和非負數排序
	private int negativeAndNotNegativeSort(int[] nums) {
		int left = 0, right = nums.length - 1;
		while (left <= right) {
			if (nums[left] >= 0) {
				right = findFirstNegativeIndexFromRight(nums, right);
				if (right < left) {
					break;
				}
				swap(nums, left, right);
				left++;
				right--;
			} else {
				left++;
			}
		}
		return left;
	}

	private int findFirstNegativeIndexFromRight(int[] nums, int right) {
		while (right >= 0) {
			if (nums[right] < 0) {
				return right;
			}
			right--;
		}
		return -1;
	}

	private void swap(int[] nums, int a, int b) {
		int t = nums[a];
		nums[a] = nums[b];
		nums[b] = t;
	}

	// 零和正數排序
	private void zeroAndPositiveSort(int[] nums, int left) {
		int right = nums.length - 1;
		while (left <= right) {
			if (nums[left] != 0) {
				right = findFirstZeroIndexFromRight(nums, right);
				if (right < left) {
					break;
				}
				swap(nums, left, right);
				left++;
				right--;
			} else {
				left++;
			}
		}
	}

	private int findFirstZeroIndexFromRight(int[] nums, int right) {
		while (right >= 0) {
			if (nums[right] == 0) {
				return right;
			} else {
				right--;
			}
		}
		return -1;
	}

	public static void main(String[] args) {
		SortBySymbol sortBySymbol = new SortBySymbolSolutionTwoTimesSort();
//		int[] nums = RandomIntArrayGenerator.random(100000000);
//		long start = System.currentTimeMillis();
//		sortBySymbol.sortBySymbol(nums);
//		long cost = System.currentTimeMillis() - start;
//		System.out.println(cost);
//		System.out.println(SortBySymbolJudge.judge(nums));
//		for (int i = 0; i < nums.length; i++) {
//			System.out.println(nums[i]);
//		}
		SortBySymbolJudge.judge(sortBySymbol);
	}

}

寫一個類測試代碼是否正確,首先爲了測試方便將兩個類的類型統一:

package org.cc11001100.alg.sortBySymbol;

public interface SortBySymbol {

	int[] sortBySymbol(int[] nums);

}

而後是生成測試數據的類:

package org.cc11001100.alg.sortBySymbol;

/**
 * 用於生成測試數據
 *
 * @author CC11001100
 */
public class RandomIntArrayGenerator {

	public static int[] random(int length) {
		int[] result = new int[length];
		for (int i = 0; i < result.length; i++) {
			int n = (int) (Math.random() * 100);
			int t = n % 5;
			if (t < 2) {
				// n ∈ [0, 1]
				n *= -1;
			} else if (t == 2) {
				// n == 2
				n = 0;
			} else {
				// n ∈ [3, 4]
				// n = n;
			}
			result[i] = n;
		}
		return result;
	}

	public static void main(String[] args) {

		int[] nums = random(20);
		for (int i = 0; i < nums.length; i++) {
			System.out.println(nums[i]);
		}

	}

}

而後是測評類:

package org.cc11001100.alg.sortBySymbol;

/**
 * 用於評測SortBySymbol輸出結果是否正確
 *
 * @author CC11001100
 */
public class SortBySymbolJudge {

	public static boolean judge(int[] nums) {
		int index = 0;
		if (nums[index] < 0) {
			while (index < nums.length) {
				if (nums[index] >= 0) {
					break;
				}
				index++;
			}
		}

		if (index < nums.length && nums[index] == 0) {
			while (index < nums.length) {
				if (nums[index] > 0) {
					break;
				}
				index++;
			}
		}

		while (index < nums.length) {
			if (nums[index] <= 0) {
				return false;
			}
			index++;
		}

		return index == nums.length;
	}

	public static void judge(SortBySymbol sortBySymbol) {
		int[] numLengths = new int[]{
				1, 2, 3, 5, 10, 1000, 10000, 100000, 1000000, 10000000, 100000000, 100000000, 100000000,
				200000000, 200000000, 200000000, 300000000
		};

		for (int i = 0; i < numLengths.length; i++) {
			int[] nums = RandomIntArrayGenerator.random(numLengths[i]);
			long start = System.currentTimeMillis();
			nums = sortBySymbol.sortBySymbol(nums);
			long cost = System.currentTimeMillis() - start;
			boolean judgeResult = judge(nums);
			System.out.println("num length=" + numLengths[i] + ", cost=" + cost + "ms, judge=" + judgeResult);
		}
	}
}

測評結果:

image

image

能夠看到,對於使用額外內存的解法當數據量足夠大時它就撐不住了,不少問題就是這樣,沒有CPU資源能夠跑慢一點,終究仍是可以跑起來的,可是沒有內存資源根本跑都跑不起來。

 

.

相關文章
相關標籤/搜索