[英]Implementing the Dutch National Flag Program with Linked Lists
我想對包含0,1s或2s的鏈表進行排序。 現在,這顯然是荷蘭國旗問題的一個變種。 http://en.wikipedia.org/wiki/Dutch_national_flag_problem
與鏈接中給出的算法相同的算法是:
“讓頂層組從陣列頂部向下生長,底部組從底部長大,並保持中間組位於底部之上。算法將位置存儲在頂部組的正下方,就在底部之上,並且在三個索引中間的正上方。在每一步中,檢查中間正上方的元素。如果它屬於頂部組,則將其與頂部下方的元素交換。如果它屬於底部,則將其與元素交換如果它在中間,請保留它。更新相應的索引。復雜性是Θ(n)移動和檢查。“
為此提供的C ++實現是:
void threeWayPartition(int data[], int size, int low, int high) {
int p = -1;
int q = size;
for (int i = 0; i < q;) {
if (data[i] == low) {
swap(data[i], data[++p]);
++i;
} else if (data[i] >= high) {
swap(data[i], data[--q]);
} else {
++i;
}
}
}
我唯一的問題是我們如何遍歷鏈表中,就像我們在數組中一樣?
給定鏈表單元格時,標准的單鏈表不允許您向后移動。 但是,您可以使用雙向鏈表,其中每個單元存儲下一個和前一個指針。 這樣您就可以向前和向后導航列表。
但是,對於您試圖解決的特定問題,我認為這不是必要的。 數組和鏈表上的算法之間的一個主要區別是,在使用鏈表時,您可以重新排列列表中的單元格以重新排序列表中的元素。 因此,您在上面詳述的算法 - 通過更改數組的內容來工作 - 實際上可能不是鏈接列表上最優雅的算法。
如果您確實使用鏈接列表,解決此問題的一種可能方法如下:
這沒有內存分配,純粹通過重新排列鏈表單元格來工作。 它仍然在時間Θ(n)中運行,這是另一個加號。 此外,您可以在不必向后走的情況下執行此操作(即,這適用於單鏈表)。
我將把完整的實現留給你,但作為一個例子,這里有簡單的C ++代碼將鏈表單元格分配到零,一和兩個列表中:
struct Cell {
int value;
Cell* next;
}
/* Pointers to the heads of the three lists. */
Cell* lists[3] = { NULL, NULL, NULL };
/* Distribute the cells across the lists. */
while (list != NULL) {
/* Cache a pointer to the next cell in the list, since we will be
* rewiring this linked list.
*/
Cell* next = list->next;
/* Prepend this cell to the list it belongs to. */
list->next = lists[list->value];
lists[list->value] = list;
/* Advance to the next cell in the list. */
list = next;
}
希望這可以幫助!
正如其他人所說,沒有反向鏈接就無法在鏈表中“備份”。 雖然它不是您問題的答案,但是可以通過三個隊列實現具有三個存儲桶的存儲桶排序來輕松完成排序。
隊列(副推送堆棧)的優點是排序穩定 。 也就是說,如果列表節點中存在數據(除了0,1,2值的鍵之外),則每個鍵的這些數據將保持相同的順序。
這只是許多情況中的一種,其中數組的規范算法不是列表的最佳算法。
有一種非常靈活,簡單的方法來實現隊列:循環鏈接列表,其中第一個節點,比如p
,是隊列的尾部 ,因此p->next
是頭部。 有了這個,代碼簡潔。
#include <stdio.h>
#include <stdlib.h>
typedef struct node_s {
struct node_s *next;
int val;
int data;
} NODE;
// Add node to tail of queue q and return the new queue.
NODE *enqueue(NODE *q, NODE *node)
{
if (q) {
node->next = q->next;
q->next = node;
}
else node->next = node;
return node;
}
// Concatenate qa and qb and return the result.
NODE *cat(NODE *qa, NODE *qb)
{
NODE *head = qa->next;
qa->next = qb->next;
qb->next = head;
return qb;
}
// Sort a list where all values are 0, 1, or 2.
NODE *sort012(NODE *list)
{
NODE *next = NULL, *q[3] = { NULL, NULL, NULL};
for (NODE *p = list; p; p = next) {
next = p->next;
q[p->val] = enqueue(q[p->val], p);
}
NODE *result = cat(q[0], cat(q[1], q[2]));
// Now transform the circular queue to a simple linked list.
NODE *head = result->next;
result->next = NULL;
return head;
}
int main(void)
{
NODE *list = NULL;
int N = 100;
// Build a list of nodes for testing
for (int i = 0; i < N; ++i) {
NODE *p = malloc(sizeof(NODE));
p->val = rand() % 3;
p->data = N - i; // List ends up with data 1,2,3,..,N
p->next = list;
list = p;
}
list = sort012(list);
for (NODE *p = list; p; p = p->next)
printf("key val=%d, data=%d\n", p->val, p->data);
return 0;
}
現在這是一個完整的簡單測試,它運行得很好。
這是未經測試的。 (如果我有時間,我會嘗試測試它。)但它應該至少非常接近解決方案。
使用雙向鏈表。 如果您已經實現了鏈接列表對象和相關鏈接列表節點對象,並且能夠在向前方向上遍歷它,那么反向遍歷並不是一大堆工作。
假設您有一個Node對象,有點像:
public class Node
{
public Node Next;
public Object Value;
}
然后,您真正需要做的就是更改Node類,然后稍微插入方法以跟蹤之前出現的Node:
public class Node
{
public Node Next;
public Node Previous;
public Object Value;
}
public void Insert(Node currentNode, Node insertedNode)
{
Node siblingNode = currentNode.Next;
insertedNode.Previous = currentNode;
insertedNode.Next = siblingNode;
if(siblingNode!= null)
siblingNode.previous = insertedNode;
currentNode.next = insertedNode;
}
PS抱歉,我沒有注意到包含C ++內容的編輯所以它更多是C#
通過CHANGING NODES而非NODE DATA適用於所有情況..希望它永遠不會太晚!
METHOD(To throw some light on handling corner cases):
1. Keep three dummy nodes each for 0,1,2;
2. Iterate throught the list and add nodes to respective list.
3. Make the next of zero,one,two pointers as NULL.
4. Backup this last nodes of each list.
5. Now handle 8 different possible cases to join these list and Determine the HEAD.
zero one two
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1
用C ++實現的。
Node* sortList(Node *head)
{
struct Node dummyzero,dummyone,dummytwo;
dummyzero.next = dummyone.next = dummytwo.next = NULL;
struct Node *zero =&dummyzero,*one = &dummyone,*two=&dummytwo;
Node *curr = head,*next=NULL;
while(curr)
{
next = curr->next;
if(curr->data==0)
{
zero->next = curr;
zero = zero->next;
}
else if(curr->data==1)
{
one->next = curr;
one = one->next;
}
else
{
two->next = curr;
two = two->next;
}
curr = next;
}
zero->next = one->next = two->next =NULL; //Since this dummynode, No segmentation fault here.
Node *zerolast = zero,*onelast = one,*twolast = two;
zero = dummyzero.next;
one = dummyone.next;
two = dummytwo.next;
if(zero==NULL)
{
if(one==NULL)
head = two;
else
{
head = one;
onelast->next = two;
}
}
else
{
head = zero;
if(one==NULL)
zerolast->next = two;
else
{
zerolast->next = one;
onelast->next = two;
}
}
return head;
}
這個想法是使用荷蘭旗幟排序算法,稍作修改:
根據荷蘭國旗法排序0和1,
但對於2而不是在列表末尾添加它們,請將它們保存在單獨的鏈表中。
最后將2的列表附加到0和1的排序列表中。
Node * sort012_linked_list(Node * head) {
if (!head || !head->next)
return head;
Node * head_of_2s = NULL;
Node * prev = NULL;
Node * curr = head;
while (curr) {
if (curr->data == 0) {
if (prev == NULL || prev->data == 0) {
prev = curr;
curr = curr->next;
}
else {
prev->next = curr->next;
curr->next = head;
head = curr;
curr = prev->next;
}
}
else if (curr->data == 1) {
prev = curr;
curr = curr->next;
}
else { // curr->data == 2
if (prev == NULL) {
head = curr->next;
curr->next = head_of_2s;
head_of_2s = curr;
curr = head;
}
else {
prev->next = curr->next;
curr->next = head_of_2s;
head_of_2s = curr;
curr = prev->next;
}
}
}
if (prev)
prev->next = head_of_2s;
return head;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.