交换单链表中的节点
Posted
技术标签:
【中文标题】交换单链表中的节点【英文标题】:Swap nodes in a singly-linked list 【发布时间】:2013-02-25 06:59:29 【问题描述】:我正在尝试交换两个节点。例如,如果节点是 a
和 b
,我将传递指针(a-1)->next
和 (b-1)->next
,它们基本上是节点 a
和 b
。
void swap(struct stack **a,struct stack **b)
struct stack *temp1 = *a, *temp2 = *b, *temp3 = *b;
*a = *b;
(*b)->next = (temp1)->next;
temp2 = temp1;
(temp2)->next = temp3->next;
我做错了什么?当我在调用函数后尝试打印节点时,它是一个无限循环。请帮忙。
【问题讨论】:
虽然你可以只处理数据,但如果你想代替节点! 那里没有多少 C++,只有 C。如果你使用 C++,你应该使用std::stack
,并使用现有的std::swap
。
我想简单地交换数据会容易得多:data_type temp = (*a)->data; (*a)->data = (*b)->data; (*b)->data = temp;
@guys 我的代码有什么问题.. :)
@us2012- 不使用它的原因是因为你被要求理解它。这可能是一个学校项目,教授不允许 OP 这样做,直到 OP 自己完成。我理解反对这种教学方式的论点,因为我已经在这里读过一百遍了。这并没有改变我们中的一些人,包括我在内,都受制于它的事实。没有规定您必须使用STL
,这只是最聪明的做法。
【参考方案1】:
为什么是无限循环?
无限循环是因为列表中的自循环之后调用swap()
函数。在swap()
代码下面的语句是错误的。
(*b)->next = (temp1)->next;
为什么?:因为在swap()
函数temp1
的赋值语句之后,next开始指向b
节点。而node[b]
的下一个指向它自己的循环。而self loop是infinite loop的原因,在你的代码中你遍历链表的地方。
我在下面绘制以展示swap()
的工作原理。这可能有助于您了解您的错误:
你没有提到,但我假设链表在 a
和 b
之间具有以下关系:(阅读红色 cmets)
(第 1 步):
+----+----+----+ +---+----+----+
| one |----->| two |
+----+----+----+ +---+---+-----+
^ ^ ^ ^
| | | |
| *a | *b
| |
temp1 temp2, temp3 "after assignment to temp variables"
(step-2): ^
|
*a = *b | *a "<--- next step"
(第 3 步): 错误声明
(*b)->next = (temp1)->next; "Change link: (temp1)->next; is `two` node"
" *b is `two`, So Self loop"
+----+----+----+ +---+----+----+ <---|
| one | | two |-----|
+----+----+----+ +---+---+-----+
^ ^ ^ ^
| | | |
| | *b *a
| |
temp1 temp2, temp3 " after assignment to temp"
看到(temp1)->next;
实际上是b
并且您通过执行(*b)->next = (temp1)->next;
来分配(*b)->next = (*b)
,因此添加了一个自循环。
(第 4 步):
我认为通过该图,您可以轻松理解 swap()
代码的最后两行在做什么:
temp2 = temp1;
(temp2)->next = temp3->next;
以下是我对这两行的图表:
temp2 = temp1;
+----+----+----+ +---+----+----+ <---|
| one | | two |-----| "<--- Self loop"
+----+----+----+ +---+---+-----+
^ ^ ^ ^
| | | |
| | *b *a
| |
temp2 = temp1; temp3
(第 5 步): 函数swap()
的最后一行左循环如下:
(temp2)->next = temp3->next; " last line of your code"
+----+----+----+ +---+----+----+ <---|
| one |----->| two |-----| "<-- Self loop"
+----+----+----+ +---+---+-----+
^ ^ ^ ^
| | | |
| | *b *a
| |
temp2 = temp1; temp3
所以循环仍然在two
节点,所以无限循环。
如何交换单链表中的两个节点?
一种方法是交换节点的数据,而不是交换节点在链表中的位置(正如我对您的问题的评论)。但是你想交换节点在列表中的位置。 好这个好!如果节点数据较大,那么最好交换节点的位置而不是交换节点的数据(交换数据将是不好的选择)
因为您有单链表,要交换列表中的任意两个任意节点,您需要那里也有前一个节点地址。 (这是你在交换逻辑中没有考虑到的一点)
为什么需要以前的指针?: 假设在一些成功的 insert(push) 操作之后,您的列表如下:
0 <--------TOP - "head"
9 <--p
2
6 <--q
5
水平图 - 假设 您要交换 说两个节点 (q)
和 (p)
:
+---+ +---+ +---+ +---+ +---+
| 0 |--->| 9 |--->| 2 |--->| 6 |--->| 5 |---
+---+ +---+ +---+ +---+ +---+ |
^ ^ ^ null
| | |
| (q) (p)
(head)
正如我所说,要交换我们需要以前的指针。您需要考虑以下
(理论上,我正在为特定节点 (p)
和 (q)
编写代码只是为了保持解释简单。但我的实现是一般性的):
在列表中的前一个指针中:
node[0] points to node[9] that is (q), and
node[2] points to node[6] that is (p)
还有
node[9] points to node[2]
node[6] points to node[5]
注意:如果你想交换两个节点,比如node[ 9 ]
和node[ 6 ]
,那么你应该使用这两个节点之前的节点的指针。
例如:两个交换node[ 9 ]
和[ 6 ]
,还需要更改上图中node[ 0 ]
的next指针和node[ 2 ]
的next指针。
交换这两个节点后的列表会怎样?
+---+ +---+ +---+ +---+ +---+
| 0 |--->| 6 |--->| 2 |--->| 9 |--->| 5 |---
+---+ +---+ +---+ +---+ +---+ |
^ ^ ^ null
| | |
| (p) (q)
(head)
以前的节点 [o]
和 [2]
现在是什么?
交换后,列出前一个指针
node[0] points to node[6] that is (q), and
node[2] points to node[9] that is (p)
和
node[9] points to node[5]
node[6] points to node[2]
所以如果你想交换两个节点;前一个节点也有影响,因为列表是单链接列表,所以你也需要以前的指针。
如何找到以前的节点指针?
假设您要交换任意两个节点node[p]
和node[q]
,那么您可以使用head pointer
查找上一个节点。
所以交换函数语法(在我的实现中)是这样的:
void swap(struct stack **head, // head node
struct stack **a, // first candidate node to swap
struct stack **b); // first candidate node to swap
你会调用这样的函数:
swap(&head, &p, &q);
定义:(要理解代码,请阅读我在几乎每一行添加的 cmets)
void swap(struct stack **head,
struct stack **a,
struct stack **b)
// first check if a agrgument is null
if( (*head) == NULL || // Empty list
(*a) == NULL || (*b) == NULL) // one node is null
// Nothing to swap, just return
printf("\n Nothing to swap, just return \n");
return;
// find previos nodes
struct stack* pre_a = get_prevnd(*head, *a);
struct stack* pre_b = get_prevnd(*head, *b);
//Now swap previous node's next
if(pre_a) pre_a->next = (*b); // a's previous become b's previous, and
if(pre_b) pre_b->next = (*a); // b's previous become a's previous
//Now swap next fiels of candidate nodes
struct stack* temp = NULL;
temp = (*a)->next;
(*a)->next = (*b)->next;
(*b)->next = temp;
//change head: if any node was a head
if((*head)==(*a))
*head = *b;
else
if((*head)==(*b))
*head = *a;
在swap()
函数中,您可以注意到我调用了一个辅助函数get_prevnd(, );
。此函数返回列表中前一个节点的地址。在函数get_prevnd(, );
中,第一个参数是列表头,第二个参数是您要查找的节点。
// find previous node function()
struct stack* get_prevnd(
struct stack* head,
struct stack* a
)
if(head == a)
// node[a] is first node
return NULL;
struct stack* temp = head; // temp is current node
struct stack* pre_a = NULL;
while(temp && temp!=a) //search while not reach to end or the node
pre_a = temp; // find previous node
temp = temp->next;
if(temp!=a)// node[a] not present in list
fprintf(stderr, "\n error: node not found!\n");
exit(EXIT_FAILURE); // bad technique to exit()
return pre_a;
幸运的是,代码正在运行 :)。以下是此代码的在线测试链接。我已经测试了各种输入。
CodePad:To Swap node in single linked list. 请检查输出。
抱歉英语不好
【讨论】:
如果您需要更多帮助,请告诉我。我觉得@thkala 给了你正确的答案我只是想告诉你代码中的错误 我会 +1 这个,只是为了 ASCII 艺术 :-) 是的,你是对的。但是@thkala 给出的代码......似乎无法正常工作。我的意思是a做了一个堆栈推送(&head,5);推(&头,6); q=头;推(&头,2);推(&头,9); p=头;推(&头,0);我最初用 swap(&p,&q) 调用堆栈是 0 9 2 6 5 但我得到的结果是 0 9 5 而我应该得到的是 0 6 2 9 5 非常感谢您的帮助,但我现在发现了一个新东西。thkalas 代码可以工作,但基本上调用该函数是不行的。 @GrijeshChauhan 非常感谢您的帮助:)【参考方案2】: "KING KHAN"
SwapanyTwoNodeData(int, int);
这个函数把两个整数作为参数,交换它们
节点数据。
1->2->6->3->4->5
SwapanyTwoNodeData (2,4);
1->3->6->2->4->5
100% 工作功能。 . . .享受。
void Swap_Any_Two_Locations_data(int x,int x1)
if(head!=NULL)
int count=1,count1=1;
Node *temp,*temp1;
temp=head;
temp1=head;
while(temp->next!=NULL && count!=x )
temp=temp->next;
count++;
while(temp1->next!=NULL && count1!=x1)
temp1=temp1->next;
count1++;
if(count==x && count1==x1)
int z=0;
z=temp->info;
temp->info=temp1->info;
temp1->info=z;
cout<<"Data Swapped"<<endl;
【讨论】:
【参考方案3】:我希望剪切和粘贴代码,但没有找到。如果有人仍然感兴趣,这就是答案。我对所有 3 个案例进行了蛮力分析。
// swaps nodes at locations *sp and *tp
struct stack *s = *sp; // store locations to prevent overwritten bugs
struct stack *t = *tp;
if(&t->next == sp) // order u (tp)-> t (sp)-> s -> ...
t->next = s->next;
s->next = t;
*tp = s;
else if(&s->next == tp) // order u (sp)-> s (tp)-> t -> ...
s->next = t->next;
t->next = s;
*sp = t;
else // disconnected u (sp)->s -> ..., v (tp)->t -> ...
struct stack *next = s->next;
s->next = t->next;
t->next = next;
*sp = t;
*tp = s;
// If you track the end of your list, it be the one pointing to NULL.
if(s->next == NULL)
end = &s->next;
else if(t->next == NULL)
end = &t->next;
注意:如果 sp == tp,此代码有效,但假设您没有 BOTH &s->next == tp && &t->next == sp(从循环开始)的病态情况。
【讨论】:
以上是关于交换单链表中的节点的主要内容,如果未能解决你的问题,请参考以下文章
2.设计一个算法,将单链表中结点以逆序排列。逆序的单链表中的结点均为原表中的结点
线性表练习之Example039-删除循环单链表中的所有最小值节点直至链表为空