寻找更好的Z字形阵列方法

时间:2016-02-19 19:36:30

标签: java arrays algorithm

一个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不感兴趣。

3 个答案:

答案 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(n / 2)=&gt; O(n)
  • 时间:时间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);
    }
}