LeetCode 31. Next Permutation 找到一个数组的下一个更大的字典序排序

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place and use only constant extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1


方法一:根据字典序规律来编程

找到从前往后最大序号的一个元素(假设序号为j),从后往前找值和序号均比它大的元素(假设序号为i),将这两个元素交换后,还要将序号为j之后的所有元素升序排序。

这样找得到的序列是下一个更大的字典序排序是因为i是从前往后找其后还有比它自身值更大的元素的最大序号,这样当从后往前找时在i之后不会再存在一个元素,其后还有元素比它自身的值更大。这样的结果就是nums[j]已经是i之后所有元素中最小的比nums[i]大的元素了。简而言之,就是找到一个元素,在该元素之后还有元素比它值更大,且该元素已经是满足前面条件的最大序号元素,然后将该元素与其后比它大的元素中值最小的元素互换位置,最后将交换之后序号在i之后的所有元素按升序排序即可。

class Solution {
public:
	//找到从前往后最大序号的一个元素(假设序号为j),从后往前找值和序号均比它大的元素(假设序号为i),将这两个元素交换后,还要将序号为j之后的所有元素升序排序
	//这样获得的才是next greater permutation of numbers.
	void nextPermutation(vector<int>& nums) {
		int len = nums.size();
		if (len <= 1)
			return;

		bool isgreatest = true;
		int record_left = 0, record_right = 0; //record_left,record_right要是能换的最大序号,
		for (int i = len - 1; i > 0; --i) {
			for (int j = i - 1; j >= 0; --j) {
				if (nums[j] < nums[i]) {
					if (j > record_left || (j == record_left && i >= record_right)) {
						record_left = j;
						record_right = i;
						isgreatest = false;
					}
				}
			}
		}

		if (isgreatest) {
			//如果已经是最大字典序了,则返回最小字典序
			reverse(nums);
		}
		else {
			swap(nums[record_left], nums[record_right]);
			sort(nums, record_left+1);
		}
	}

	void swap(int &a, int&b) {
		int temp = a;
		a = b;
		b = temp;
	}

	void sort(vector<int> &nums, int begin) {
		if (begin >= nums.size() - 1)
			return;

		int tmp = 0;
		int lastExchangeIndex = 0;//记录最后一次交换的位置
		int sortBorder = nums.size() - 1;//无序数组的边界,每次比较只需要比较到这里为止
		for (int i = begin; i < nums.size(); ++i) {
			//有序标记,每一轮的初始是true
			bool issorted = true;
			for (int j = begin; j < sortBorder; ++j) {
				if (nums[j] > nums[j + 1]) {
					//有元素交换,所以不是有序,标记为false
					issorted = false;
					swap(nums[j], nums[j + 1]);
					//把无序数列的边界更新为最后一次交换元素的位置
					lastExchangeIndex = j;
				}
			}
			sortBorder = lastExchangeIndex;
			if (issorted)
				break; //如果已经是有序的了,直接结束循环,不需要继续后面的比较了
		}
	}

	void reverse(vector<int> &nums) {
		int left = 0, right = nums.size() - 1;
		while (left <= right) {
			swap(nums[left], nums[right]);
			++left;
			--right;
		}
	}
};

Complexity Analysis

  • Time complexity : O(n). In worst case, only two scans of the whole array are needed.

  • Space complexity : O(1). No extra space is used. In place replacements are done.

    扫描二维码关注公众号,回复: 3422289 查看本文章

方法二:与方法一原理相同,换一种实现方式

  1. Start from its last element, traverse backward to find the first one with index i that satisfy num[i-1] < num[i]. So, elements from num[i] to num[n-1] is reversely sorted.
  2. To find the next permutation, we have to swap some numbers at different positions, to minimize the increased amount, we have to make the highest changed position as high as possible. Notice that index larger than or equal to i is not possible as num[i,n-1] is reversely sorted. So, we want to increase the number at index i-1, clearly, swap it with the smallest number between num[i,n-1] that is larger than num[i-1]. For example, original number is 121543321, we want to swap the '1' at position 2 with '2' at position 7.
  3. The last step is to make the remaining higher position part as small as possible, we just have to reversely sort the num[i,n-1]

JAVA代码实现如下:

public class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        while (i >= 0 && nums[i + 1] <= nums[i]) {
            i--;
        }
        if (i >= 0) {
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            swap(nums, i, j);
        }
        reverse(nums, i + 1);
    }

    private void reverse(int[] nums, int start) {
        int i = start, j = nums.length - 1;
        while (i < j) {
            swap(nums, i, j);
            i++;
            j--;
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

C++代码如下:

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
    	int n = nums.size(), k, l;
    	for (k = n - 2; k >= 0; k--) {
            if (nums[k] < nums[k + 1]) {
                break;
            }
        }
    	if (k < 0) {
    	    reverse(nums.begin(), nums.end());
    	} else {
    	    for (l = n - 1; l > k; l--) {
                if (nums[l] > nums[k]) {
                    break;
                }
            } 
    	    swap(nums[k], nums[l]);
    	    reverse(nums.begin() + k + 1, nums.end());
        }
    }
}; 

方法三:直接使用C++库函数

Solution 1

Just for info: There's a library function that does the job, even going from totally reverse sorted to sorted:

void nextPermutation(vector<int>& nums) {
    next_permutation(begin(nums), end(nums));
}

Solution 2

Using library functions for all building blocks of the algorithm. Very nice how they all play together, notice the total lack of +1/-1, it all fits exactly.

void nextPermutation(vector<int>& nums) {
    auto i = is_sorted_until(nums.rbegin(), nums.rend());
    if (i != nums.rend())
        swap(*i, *upper_bound(nums.rbegin(), i, *i));
    reverse(nums.rbegin(), i);
}

Solution 3

Doing it all on my own (except swap, let's not be silly):

void nextPermutation(vector<int>& nums) {
    int i = nums.size() - 1, k = i;
    while (i > 0 && nums[i-1] >= nums[i])
        i--;
    for (int j=i; j<k; j++, k--)
        swap(nums[j], nums[k]);
    if (i > 0) {
        k = i--;
        while (nums[k] <= nums[i])
            k++;
        swap(nums[i], nums[k]);
    }
}

Solution 4

Ok, let's be silly after all and not even use swap :-)

void nextPermutation(vector<int>& nums) {
    int i = nums.size() - 1, k = i, tmp;
    while (i > 0 && nums[i-1] >= nums[i])
        i--;
    for (int j=i; j<k; j++, k--)
        tmp = nums[j], nums[j] = nums[k], nums[k] = tmp;
    if (i > 0) {
        k = i--;
        while (nums[k] <= nums[i])
            k++;
        tmp = nums[i], nums[i] = nums[k], nums[k] = tmp;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_25800311/article/details/82623682