使用下标对数组进行排序而不移动数组元素

时间:2017-06-13 15:21:32

标签: c arrays linked-list mergesort

我必须使用C中的mergesort对一组非负的int进行排序,但是有一个问题 - 我无法移动实际的数组元素,就好像我有{3,5,6,7,0 ,4,1,2},所需的输出应为

第一个元素是下标:4

0 3 5

1 5 2

2 6 3

3 7 -1

4 0 6

5 4 1

6 1 7

7 2 0

查看原始输入的顺序是如何保持不变的,但只有在比较数字时才会交换键?到目前为止,我的主要职能是:

void Merge(int *A,int *L,int leftCount,int *R,int rightCount) 

{

    int i,j,k;

    // i - to mark the index of left sub-array (L)
    // j - to mark the index of right sub-array (R)
    // k - to mark the index of merged sub-array (A)
    i = 0; j = 0; k =0;

    while(i<leftCount && j< rightCount)
    {
        if(L[i]  <=  R[j])
        {
            //something important;
             i++;
        }
        else
        {
            //something important;
            j++;
        }
    }

    i=0;
    j=0;
    while(i < leftCount) A[k++] = L[i++]; //merge all input sequences without swapping initial order

    while(j < rightCount) A[k++] = R[j++];

}

// Recursive function to sort an array of integers.
void MergeSort(int *A,int n) 

{

    int mid,i,k, *L, *R;

    if(n < 2)
    {
       return; 
    }


    mid = n/2;  // find the mid index.

    // create left and right subarrays
    // mid elements (from index 0 till mid-1) should be part of left sub-array
    // and (n-mid) elements (from mid to n-1) will be part of right sub-array
    L = (int*)malloc(mid*sizeof(int));
    R = (int*)malloc((n- mid)*sizeof(int));

    for(i = 0;i<mid;i++) L[i] = A[i]; // creating left subarray
    for(i = mid;i<n;i++) R[i-mid] = A[i]; // creating right subarray

    MergeSort(L,mid);  // sorting the left subarray
    MergeSort(R,n-mid);  // sorting the right subarray
    Merge(A,L,mid,R,n-mid);  // Merging L and R into A as sorted list.
        free(L);
        free(R);
}

我知道在合并排序期间只有单个元素时,我必须将递归树底部的所有元素的索引初始化为-1。然后我必须相应地更改这些索引,因为我比较左数组和右数组中的数组元素。但那就是我陷入困境的地方。我的教授告诉班级要使用一个链表 - 但我很难想象我如何实现一个链表来实现这个索引的事情。我不希望我的家庭作业由其他人完成,我只是希望有人用伪代码解释我应该如何解决它,然后我可以自己编写实际的代码。但我很失落,我很抱歉,如果这个问题很难被问到,但是我的品牌在这里崭露头角,我吓坏了:(

3 个答案:

答案 0 :(得分:1)

确定。让我们从一个简单的例子开始,列出4个要排序的元素,让我们看看你的函数需要做什么以及如何根据链表进行处理:

#->[3]->[1]->[4]->[2]->$

好的,所以这里#是指向第一个元素的指针,在本例中是[3],它有一个指向第二个元素的指针,依此类推。我将->$用作空指针(不指向任何东西)和->*作为'我不关心'指针(指针可能存在,但想要显示概念中断)列表)

我们现在执行多次传递以将这些传递合并到一个排序列表中。

这是第一次传递,因此我们将其视为多个长度为1的列表:

#->* [3]->* [1]->* [4]->* [2]->*

实际上,这些仍然存在,但这是概念模型。

那么我们现在需要'知道'什么?

  1. 列表#1
  2. 之前的列表末尾
  3. 参考列表#1的开头
  4. 参考列表#2的开头
  5. 参考列表#2之后的项目
  6. 然后我们将两个子列表(2)和(3)合并到(1)的末尾,取最小的列表头部,将其分离,并将其修改为(1),移动到下一个该列表中的值(如果存在)

    概念

    //sublists length 1. we'll work on the first pair
    #->* [3]->* [1]->* [4]->* [2]->*  
    
    //smallest element from sublists added to new sublist
    #->* [3]->*        [4]->* [2]->*  //
         [1]->*
    
    //above repeated until sublists are both exhausted
    #->*               [4]->* [2]->*
         [1]->[3]->*
    
    //we now have a sorted sublist
    #->* [1]->[3]->*   [4]->* [2]->*
    

    实际

    //(1-4) are pointers to the list as per description above
    #->[3]->[1]->[4]->[2]->$
    |   |    |    |
    1   2    3    4
    
    //set the end of the lists (2) and (3) to point to null, so 
    //when we reach this we know we have reached the end of the 
    //sublist (integrity remains because of pointers (1-4)
    #->* [3]->$ [1]->$ [4]->[2]->$
    |     |      |      |
    1     2      3      4
    
    //the smallest (not null) of (2) and (3) is referenced by (1), 
    //update both pointers (1) and (2) or (3) to point to the next
    //item
    #->[1]->* [3]->$ $ [4]->[2]->$
        |       |    |  |
        1       2    3  4
    
    //repeat until both (2) and (3) point to null   
    #->[1]->[3]->* $ $ [4]->[2]->$
             |     | |  |
             1     2 3  4
    

    我们现在有一个包含第一个子列表的链表。现在,我们跟踪(1),然后转到第二对子列表,从(4)开始,重复该过程。

    一旦(2),(3)和(4)都为空,我们就完成了传球。我们现在已经排序了子列表和单个链接列表:

    #->[1]->[3]->[2]->[4]->$ $ $ $
                       |     | | |
                       1     2 3 4
    

    现在我们也这样做,只有子列表的长度是两倍。 (并重复)

    当子列表长度&gt; =链表长度时,列表将被排序。

    在此期间,我们实际上没有移动任何数据,只修改了链表中项目之间的链接。

    这可以让你从这里了解你需要做什么。

    我已将其扩展为一些实际代码:

    See it here

    我是用python编写的,所以它满足了你对伪代码的需求,因为它不是你所写的语言的代码。

    相关功能及其他评论:

    def mergesort(unsorted):
      #dummy start node, python doesn't have pointers, but we can use the reference in here in the same way
        start = llist(None)
        start.append(unsorted)
        list_length = unsorted.length()
        sublist_length = 1
        #when there are no sublists left, we are sorted
        while sublist_length < list_length:
            last  = start
            sub_a = start.next
            #while there are unsorted sublists left to merge
            while sub_a:
                #these cuts produce our sublists (sub_a and sub_b) of the correct length
                #end is the unsorted content
                sub_b = sub_a.cut(sublist_length)
                end   = sub_b.cut(sublist_length) if sub_b else None
                #I've written this so is there are any values to merge, there will be at least one in sub_a
                #This means I only need to check sub_a
                while sub_a:
                    #sort the sublists based on the value of their first item
                    sub_a, sub_b = sub_a.order(sub_b)
                    #cut off the smallest value to add to the 'sorted' linked list
                    node = sub_a
                    sub_a = sub_a.cut(1)
                    last = last.append(node)
                    #because we cut the first item out of sub_a, it might be empty, so swap the references if so
                    #this ensures that sub_a is populated if we want to continue
                    if not sub_a:
                      sub_a, sub_b = sub_b, None
                #set up the next iteration, pointing at the unsorted sublists remaining
                sub_a = end
            #double the siblist size for the next pass
            sublist_length *=2
        return start.next
    

答案 1 :(得分:0)

创建项目的链接列表,其中每个列表项都包含每个数组项的值和索引。因此,除了prev / next之外,链接列表中的每个项目都是具有结构成员uint value;uint index;的结构。

仅通过迭代数组预先填充链接列表,并为每个数组元素添加一个新的链表项,并在每个链表项中设置数组元素的值和索引,因为它们是添加到列表中。

使用预先填充的链接列表作为实际数组值的“代理”,并将链接列表排序为原始数组。即而不是基于myArray[i]进行排序,而是根据currentLinkListItem.value进行排序。

答案 2 :(得分:0)

谈到链接列表:

typedef struct ValueNode
{
    struct ValueNode* next;
    int* value;
} ValueNode;

typedef struct ListNode
{
    struct ListNode* next;
    ValueNode* value;
} ListNode;

单链表就足够了......

现在首先是合并算法:

ValueNode* merge(ValueNode* x, ValueNode* y)
{
    // just assuming both are != NULL...
    ValueNode dummy;
    ValueNode* xy = &dummy;
    while(x && y)
    {
        ValueNode** min = *x->value < *y->value ? &x : &y;
        xy->next = *min;
        *min = (*min)->next;
        xy = xy->next;
    }
    // just append the rest of the lists - if any...
    if(x)
    {
        xy->next = x;
    }
    else if(y)
    {
        xy->next = y;
    }
    return dummy.next;
}

虚拟用于不必在循环中检查for NULL ...

现在让我们使用它:

int array[] = { 3, 5, 6, 7, 0, 4, 1, 2 };
ListNode head;
ListNode* tmp = &head;
for(unsigned int i = 0; i < sizeof(array)/sizeof(*array); ++i)
{
    // skipping the normally obligatory tests for result being 0...
    ValueNode* node = (ValueNode*) malloc(sizeof(ValueNode));
    node->value = array + i;
    node->next = NULL;
    tmp->next = (ListNode*) malloc(sizeof(ListNode));
    tmp = tmp->next;
    tmp->value = node;
}
tmp->next = NULL;

现在我们已经设置了一个列表列表,每个列表都包含一个元素。现在我们合成两个后续列表。我们需要注意:如果我们将两个列表合并为一个,将其保留为新头并将下一个列合并到其中,依此类推,那么我们就实现了选择排序!所以我们需要确保在合并所有其他数组之前我们不会触及已合并的数组。这就是为什么下一步看起来有点复杂......

while(head.next->next) // more than one single list element?
{
    tmp = head.next;
    while(tmp)
    {
        ListNode* next = tmp->next;
        if(next)
        {
            // we keep the merged list in the current node:
            tmp->value = merge(tmp->value, next->value);
            // and remove the subsequent node from it:
            tmp->next = next->next;
            free(next);
        }
        // this is the important step:
        // tmp contains an already merged list
        // -> we need to go on with the NEXT pair!
        tmp = tmp->next;
        // additionally, if we have an odd number of lists,
        // thus at the end no next any more, we set tmp to NULL,
        // too, so we will leave the loop in both cases...
    }
}

最后,我们可以打印结果;请注意,我们在外部链接列表中只剩下一个链接列表:

ValueNode* temp = head.next->value;
while(temp)
{
    printf("%d\n", *temp->value);
    temp = temp->next;
}

还缺少的是释放分配的内存 - 我会把它留给你......