题目描述:
题目要求在不使用任何内置函数的情况下解决问题,时间复杂度为 O(nlog(n))。
笔者使用了快速排序,但是直接使用最原始的快速排序,有些特殊的测试用例会超时。
1)如果数组本身基本有序,则使用原始快速排序算法选取基准的方式,时间复杂度会退化为O(n^2),所以这里使用了随机选取基准点的方式,代码为
int randomIndex = left + rand()%(right-left+1);
swap(nums, left, randomIndex);
2)如果数组中出现了重复元素,速度也会变慢,在得到基准元素、排序之后,在递归调用排序函数前,先去除位置与基准相近、值与基准相同的元素,为什么选取基准呢?笔者认为其他位置的元素不是特别固定,选取基准后、进行一轮排序后,基准前后的元素与基准之间的大小关系是固定的,如果数组中有元素重复,那么在固定基准及其位置后,前后与基准相同的元素与数组前面或后面的大小关系也是确定的,其位置也不用调整了。
所以,最终代码是在原始快速排序基础上加上了随机选取基准点、去除重复元素,代码如下所示:
class Solution {
public:
void swap(vector<int>& nums, int i, int j) {
if(i == j) {
return;
}
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
void quicksort(vector<int>& nums, int left, int right) {
if(left == right) {
return;
}
int i = left;
int j = right;
int randomIndex = left + rand()%(right-left+1);
swap(nums, left, randomIndex);
while(i < j) {
while((i < j) && (nums[j] >= nums[left])) {
j--;
}
while((i < j) && (nums[i] <= nums[left])) {
i++;
}
swap(nums, i, j);
}
swap(nums, left, i);
if(i-left > 1) {
int left_pivot = i - 1;
while((left_pivot > left) && (nums[left_pivot] == nums[i])) {
left_pivot--;
}
quicksort(nums, left, left_pivot);
}
if(right-i > 1) {
int right_pivot = i + 1;
while((right_pivot < right) && (nums[right_pivot] == nums[i])) {
right_pivot++;
}
quicksort(nums, right_pivot, right);
}
}
vector<int> sortArray(vector<int>& nums) {
srand(time(0));
quicksort(nums, 0, nums.size()-1);
return nums;
}
};
思考:经典算法、前人经验当然是很好的,能够解决大部分问题,但是总有新的未知的情况出现,那么如何改进经典算法解决现有问题就是一个有趣的事情,把前人的算法理解透彻、算法的每一步的作用、新问题给现有算法带来了哪些挑战、如何改进原有算法既能不影响对于原有测试用例的处理效果又能改进对于新问题的处理效果,怎么办呢,勤学好问、多思考、多见识,把有趣的问题想清楚、弄明白,不贪多求全,找准方向、方法,刻意训练。