一个zig-zag方法,它将数组作为参数并返回一个Zig-zag数组。
示例:输入2,6,1,7,9,3 产出9,1,7,2,6,3
返回的数组必须具有替代的最高数字和最低数字。
我能想到这种方法。 //伪代码
public static int [] zig-zag(int arr[])
{
arr.sort();
int returnArr[] = new int[arr.length];
int begindex = 0, endindex = arr.length -1;
int idx = 0;
while(begindex<arr.length/2-1 && endindex>=arr.length/2)
{
returnArr[idx++] = arr[endindex];
returnArr[idx++] = arr[begindex];
begindex++;endindex--;
}
if(arr.length%2 == 1)
reurnArr[idx] = arr[begindex];
return returnArr;
}
该方法的时间复杂度为O(nlogn)(因为排序)和O(n)的空间复杂度。 有没有其他方法/算法,以便它可以比O(nlogn)做得更好?或O(nlogn)和空间复杂度为O(1)?
TC O(n ^ 2)和SC O(1)还有一种方法。但对O(n ^ 2)的TC不感兴趣。
答案 0 :(得分:2)
这是一种算法,可以使用链接列表以时间复杂度O(nlogn)
和空间复杂度O(1)
来执行此操作。
该方法适用于具有重复值的列表。
如下:
首先,获取列表l
,按降序排序,后半部分反转。 (请注意,您的排序算法必须在链接列表中就位,例如in place merge sort。)
例如,对于l = 2, 6, 1, 7, 9, 3
,此表单为l = 9, 7, 6, 1, 2, 3
。如果您的列表长度奇怪,则前半部分将比第二部分长一个元素。
一种简单的方法是按降序排序l
,然后反转下半部分的元素。
接下来,我们创建一些临时变量:
Node upper = list.head; //Upper half of list pointer
Node lower = list.get(l.length/2); //Lower half of list pointer
Node temp = null; //Utility pointer to hold whatever we need
//Let's set up our initial state
list.get(l.length/2-1) = null; //Disconnect two halves of the list
temp = upper.next; //Hold upper half minus head
upper.next = lower; //First element of upper half stitched to bottom half
//Note that lower would need to be at `l.length/2+1` for an odd length list
//This also applies to list.get in the set up
//The code could be generalized to both even and odd lenghts by using `Math.ceil`
// or a similar function to round up instead of Java's default of rounding down
zigZag(upper, lower, temp); //Call to algorithm
最后,算法:
public static void zigZag(Node upper, Node lower, Node temp){
int i = 0; //Controls alternation
while(temp != null){ //Until temp gets set to null by lower.next or upper.next
if(i%2==0){ //On even iterations
upper = temp;
temp = lower.next;
lower.next = upper;
}
else{ //On odd iterations
lower = temp;
temp = upper.next;
upper.next = lower;
}
i++;
}
}
或者,这是递归版本:
public static void zigZag(Node upper, Node lower, Node temp){
if(temp == null) // temp got set to null by lower.next or upper.next
return; // we're done
upper = temp;
temp = lower.next;
lower.next = upper;
zigZag(lower, upper, temp); //swap upper/lower for next cycle
}
您现在有一个zig-zagged链表,存储在l
。
排序:时间O(nlogn),空间O(1)
撤消:时间O(n),空间O(1)
时间:时间O(1),空间O(1)
算法:时间O(n),空间O(1)
该算法只需更改每个节点的next
指针一次,因此它在O(n)时间内运行。它不会创建任何新变量,因此具有恒定的空间复杂度,O(1)。
递归版本为tail recursive,这意味着它只能使用单个堆栈帧,从理论上讲它具有恒定的空间复杂度O(1)。 (虽然不是Java,因为does not support tail-recursion optimization。)
正如您所看到的,空间复杂性始终保持不变,从而使我们的整个程序O(1)空间使用。
时间复杂度为O(nlogn)+ O(n)+ O(1)+ O(n),显然由O(nlogn)支配。
由于您使用升序排序,因此额外撤消链接列表会降低程序速度,但不会改变整体时间复杂度。
同样地,你可以提出一种排序,它可以提供所需的半降,半升,以节省一些时间,但它也不会改变整体时间复杂度。
正如his answer中的@flkes所提到的,你可以通过减少排序的时间复杂度来减少整个程序的时间复杂度,因为它会产生主导词。
如果您发现在O(n)时间内排序的实现(例如this linked-list radix sort algorithm或类似的bucket sort算法),您可以使用常量实现O(n)的总时间复杂度,O(1),空间复杂性,这真的非常好。
答案 1 :(得分:1)
我建议首先实施radix sort,其复杂度为O(n)
。一个例子可以是found here
对列表进行基本排序后,您可以使用带有简单for循环的容器轻松地将其映射到Z字形图案。这应该会将复杂性提升到某些O(n + kn)
,这仍然可以解析为O(n)
答案 2 :(得分:1)
排序后,反转阵列的后半部分:
现在剩下的问题是对数组元素做一个完美的shuffle - 一次又一次出现的问题。
如果您想要就地应用置换并知道如何转换索引,则可以保持索引的“记分板”处理 - 但即使每个项目的一个位也是O(n)存储。 (找到仍需要处理的下一个索引并执行包含它的循环,保持分数,直到处理完所有索引。)
除了数组之外,在线性时间和恒定空间中的就地完美随机播放的非常好的再现是在{CS}处Aryabhata's。该方法由Peiyush Jain提出placed at arxiv.org (作为第一步的排序的复杂性可能主导排列/洗牌步骤。)
此任务还有另一种解释,或排序步骤:排序成折叠数组
最容易为这项任务提供贷款的类型必须是双端选择类别:
在每次传递尚未放置的数据时,确定3 / 2n比较中的最小值和最大值并换成它们的位置,直到剩下一个值或根本没有值。
或者采用标准排序方法,并映射索引。对于它的地狱:
/** Anything with accessors with int parameter */
interface Indexable<T> {
T get(int index);
T set(int index, T value);
// int size(); // YAGNI?
}
/** The accessors have this folded in half,
* while iterator() is not overridden */
@SuppressWarnings("serial")
class FoldedList<T> extends ArrayList<T>
implements Indexable<T> {
public FoldedList(@SuppressWarnings("unchecked") T...elements) {
super(Arrays.asList(elements));
}
int map(int index) {
final int last = size()-1;
index = 2*index;
return last <= index ? 2*last-index : index+1;
}
@Override
public T get(int index) { return super.get(map(index)); }
@Override
public T set(int index, T element) {
return super.set(map(index), element);
}
}
/** Sort an Indexable<T> */
public class Sort {
// Hoare/Sedgewick using middle index for pivot
private static <T extends Comparable<T>>
int split(Indexable<T> ixable, int lo, int hi) {
int
mid = lo + (hi-lo)/2,
left = lo+1,
right= hi-1;
T pivot = ixable.get(mid),
l = null, r = null;
ixable.set(mid, ixable.get(lo));
scan:
while (true) {
while ((l = ixable.get(left)).compareTo(pivot) < 0)
if (right < ++left) {
left--;
break scan;
}
while (pivot.compareTo(r = ixable.get(right)) < 0)
if (--right <= left) {
left -= 1;
l = ixable.get(left);
break scan;
}
ixable.set(left, r); // place misplaced items
ixable.set(right, l);
if (--right < ++left) {
left = right;
l = r;
break;
}
}
ixable.set(lo, l); // put last left value into first position
ixable.set(left, pivot); // place pivot at split index
return left;
}
private static <T extends Comparable<T>>
void sort(Indexable<T> ixable, int lo, int hi) {
while (lo+2 < hi) { // more than 2 Ts
int split = split(ixable, lo, hi);
if (split - lo < hi - split) {
sort(ixable, lo, split); // left part smaller
lo = split + 1;
} else {
sort(ixable, split+1, hi); // right part smaller
hi = split;
}
}
T l, h;
if (lo < --hi // 2 Ts
&& (l = ixable.get(lo)).compareTo(h = ixable.get(hi)) > 0) {
ixable.set(lo, h); // exchange
ixable.set(hi, l);
}
}
public static <T extends Comparable<T>>
void main(String[] args) {
Indexable<Number> nums = new FoldedList<>( //2,6,1,7,9,3);
7, 3, 9, 3, 0, 6, 1, 2, 8, 6, 5, 4, 7);
sort((Indexable<T>) nums);
System.out.println(nums);
}
}