324. Wiggle Sort II

Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3]....

Example:
(1) Given nums = [1, 5, 1, 1, 6, 4], one possible answer is [1, 4, 1, 5, 1, 6]
(2) Given nums = [1, 3, 2, 2, 3, 1], one possible answer is [2, 3, 1, 3, 1, 2].

Note:
You may assume all input has valid answer.

Follow Up:

Can you do it in O(n) time and/or in-place with O(1) extra space?

Solution:

solve it in O(n) space is easy, we could first find the median of the nums, and partition the array by smaller | median | larger, it takes O(1) time,

we want to put median numbers in the smallest odd positions and largest even positions to separate them as far as possible to avoid nums[i] == nums[i + 1];

for the index 

0 1 2 3 4 5 6 they will end to be

1 3 5 0 2 4 6

How to get this?  from https://discuss.leetcode.com/topic/41464/step-by-step-explanation-of-index-mapping-in-java/14

This is to explain why mapped index formula is (1 + 2*index) % (n | 1)

Notice that by placing the median in it's place in the array we divided the array in 3 chunks: all numbers less than median are in one side, all numbers larger than median are on the other side, median is in the dead center of the array.

We want to place any a group of numbers (larger than median) in odd slots, and another group of numbers (smaller than median) in even slots. So all numbers on left of the median < n / 2 should be in odd slots, all numbers on right of the median > n / 2 should go into even slots (remember that median is its correct place at n / 2)

PS: I'm ignoring the discussion of odd/even array length for simplicity.

So let's think about the first group in the odd slots, all numbers is the left side of the array should go into these odd slots. What's the formula for it? Naturally it would be:
(1 + 2 x index) % n

All these indexes are less than n / 2 so multiplying by 2 and add 1 (to make them go to odd place) and then mod by n will always guarantee that they are less than n.

Original Index => Mapped Index
0 => (1 + 2 x 0) % 6 = 1 % 6 = 1
1 => (1 + 2 x 1) % 6 = 3 % 6 = 3
2 => (1 + 2 x 2) % 6 = 5 % 6 = 5

These are what's less than median, if we continue this with indexes 3, 4, 5 we will cycle again:
3 => (1 + 2 x 3) % 6 = 7 % 6 = 1
4 => (1 + 2 x 4) % 6 = 9 % 6 = 3
5 => (1 + 2 x 5) % 6 = 11 % 6 = 5

and we don't want that, so for indexes larger than n/2 we want them to be even, (n|1) does that exactly. What n|1 does it that it gets the next odd number to n if it was even
if n = 6 for example 110 | 1 = 111 = 7
if n = 7 for example 111 | 1 = 111 = 7

and this is what we want, instead of cycling the odd numbers again we want them to be even, and odd % odd number is even so updating the formula to :
(1 + 2*index) % (n | 1)

Then we have:
3 => (1 + 2 x 3) % 7 = 7 % 7 = 0
4 => (1 + 2 x 4) % 7 = 9 % 7 = 2
5 => (1 + 2 x 5) % 7 = 11 % 7 = 4

And we want to finish the conversion while the partition, so we compute each mapped index in the partition method.

Code:

public class Solution {
    public void wiggleSort(int[] nums) {
        int mid = findKth(nums,(nums.length - 1)/ 2,0,nums.length-1);
        partition(nums,mid);
    }
    
    public int mapIndex(int len, int i){
        return (2*i + 1) % (len | 1);
    }
    
    public void partition(int[] nums, int val){
        int len = nums.length;
        int l = 0;
        int r = nums.length - 1;
        int i = 0;
        while(i <= r){
            int curIndex = mapIndex(len,i);
            if(nums[curIndex] > val){
                swap(nums,curIndex,mapIndex(len,l));
                i++;l++;
            } else if(nums[curIndex] == val){
                i++;
            } else {
                swap(nums,curIndex,mapIndex(len,r));
                r--;
            }
        }
    }
    
    public void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
    
    public int findKth(int[] nums, int k, int start, int end){
        int index= partition(nums,start,end);
        if(index == k) return nums[k];
        else if(index > k) return findKth(nums,k,start,index - 1);
        else return findKth(nums,k,index + 1, end);
    }
    
    public int partition(int[] nums, int start, int end){
        if(start == end) return start;
        int p = nums[start];
        while(start < end){
            while(start < end && nums[end] > p){
                end--;
            }
            nums[start] = nums[end];
            while(start < end && nums[start] <= p){
                start++;
            }
            nums[end] = nums[start];
        }
        nums[start] = p;
        return start;
    }
}






<think> 我们已知用户当前的问题是关于Python内置的sort方法所使用的排序算法。根据之前提供的引用资料,我们并没有直接关于Python sort方法的具体算法描述,因此需要结合外部知识来回答。但根据已知的引用,我们可以参考一些排序算法的实现(如引用1、2、3中的wiggle sort、插入排序、计数排序),但注意这些并非Python sort使用的算法。 实际上,Python的sort方法使用的是Timsort算法,这是一种结合了归并排序和插入排序的混合排序算法,由Tim Peters于2002年设计并应用于Python。Timsort旨在处理现实世界中多种多样的数据情况,尤其擅长处理部分有序的数据集。 下面我将按照要求详细回答: </think> Python 的 `sort()` 方法采用 **Timsort 算法**,这是一种结合了归并排序(Merge Sort)和插入排序(Insertion Sort)优势的混合算法。以下是其核心原理和特点: --- ### Timsort 的核心设计 1. **自适应分段** 扫描数组,将连续升序/降序片段识别为「自然有序段」(run),降序段会被反转优化。例如: `[5, 9, 12, 3, 7, 15]` → 分段为 `[5,9,12]` 和 `[3,7,15]` 2. **插入排序优化小片段** 当片段长度 $min\_run < 64$ 时(经验值),使用插入排序处理。插入排序在小数据量时效率极高(时间复杂度 $O(n^2)$ 但常数项极小)[^2]。 3. **归并排序合并片段** 通过归并排序的 **平衡合并策略** 逐步合并片段: - 使用栈管理待合并片段 - 合并时保证栈顶三个片段满足: $$|Z| > |Y| + |X|$$ $$|Y| > |X|$$ 确保合并操作复杂度均衡(避免最坏情况) --- ### 性能优势 | 场景 | 时间复杂度 | 说明 | |--------------|-------------|--------------------------| | 最佳情况 | $O(n)$ | 完全有序数组 | | 平均情况 | $O(n \log n)$ | 随机数据 | | 最坏情况 | $O(n \log n)$ | 严格保证(优于快速排序) | | 空间复杂度 | $O(n)$ | 归并操作需要临时空间 | **关键优势**: - 对部分有序数据效率接近 $O(n)$ - 稳定排序(相等元素顺序不变) - 规避归并排序和快速排序的最坏情况 --- ### 示例演示 ```python data = [3, 1, 4, 1, 5, 9, 2, 6] data.sort() # 调用Timsort print(data) # 输出: [1, 1, 2, 3, 4, 5, 6, 9] ``` --- ### 设计哲学 Timsort 的核心思想源自 **现实数据通常部分有序** 的观察。通过利用数据的现有有序性,减少不必要的比较操作。其合并策略灵感源自 **平衡二叉树** ,确保合并树的深度最小化[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值