链表是最为常见,并且经常需要使用到的数据结构之一。对于单链表的基本操作,例如创建链表、插入、删除等都不是特别复杂,但是其实对于链表的操作实际上很多的操作还是有一定的难度,例如上次给出的单链表的逆转,以及这里的单链表交换两个节点。
单链表的交换节点的含义是:给定一个单链表,要求交换其中的任意两个节点。注意这里链表的头节点是不参与节点交换的。这个看上去是比较简单,但是实现起来却还是需要一定的基本功。
对于这个问题,关键是要用4个指针来保存两个交换的节点的前后节点位置,具体实现请参见实现源码。实际上,还有一个逻辑更加清晰的实现:只要用两个指针保存当前的两个交换节点的前一个节点,然后依次删除待交换节点,再在记录的前一个节点后交替插入删除的两个节点,也就是实际上将这个过程转化为了对于链表的两个基本操作就可以完成了。但是要注意的是,这个实现中当两个交换节点是相邻节点的时候会出现问题,要单独处理,具体原因手工操作一次即可得知。后一种方法这里就不给出了。
实现代码中要说明的是,交换链表节点传入的是两个交换节点指针,但是为了测试简单实现,将这两个节点换成了待交换节点的关键字(值域),再到链表中定位。
//Link.h
#include <iostream>
#include <ctime>
struct Node
{
public:
Node():_val(0),_next(NULL)
{
}
Node(int val):_val(val),_next(NULL)
{
}
Node(int val,Node* next):_val(val),_next(next)
{
}
~Node()
{
if (_next)
delete _next;
}
public:
int _val;
Node* _next;
};
typedef Node* LinkNode;
Node* CreateLink(int len,int MAX_BOUND = 100)
{
srand((unsigned int)time(NULL));
LinkNode head = new Node(-1);
LinkNode tmp = head;
for (int i = 0; i < len; ++i)
{
//tmp = tmp->_next = new Node(rand() % MAX_BOUND);
tmp = tmp->_next = new Node(i);
}
tmp->_next = NULL;
return head;
}
void ExchLinkNode(const LinkNode head,const LinkNode node1,const LinkNode node2)
{
//head不准被交换
LinkNode prenode1 = NULL; //保存待交换节点node1的前一个节点
LinkNode postnode1 = NULL; //保存待交换节点node1的后一个节点
LinkNode prenode2 = NULL; //保存待交换节点node2的前一个节点
LinkNode postnode2 = NULL; //保存待交换节点node2的后一个节点
LinkNode tmp = head;
//不得和头节点交换
if (node1 == head)
{
return ;
}
else if (node2 == head)
{
return ;
}
//自己和自己就不必交换了
if (node1 == node2)
{
return ;
}
//节点相邻情况处理
if (node1->_next == node2)
{
tmp = head;
while (tmp->_next != node1)
{
tmp = tmp->_next;
}
prenode1 = tmp;
postnode2 = node2->_next;
prenode1->_next = node2;
node2->_next = node1;
node1->_next = postnode2;
return ;
}
if (node2->_next == node1)
{
tmp = head;
while (tmp->_next != node1)
{
tmp = tmp->_next;
}
prenode2 = tmp;
postnode1 = node1->_next;
prenode2->_next = node1;
node1->_next = node2;
node2->_next = postnode1;
return ;
}
tmp = head;
while (tmp->_next != node1)
{
tmp = tmp->_next;
}
prenode1 = tmp;
tmp = head;
while (tmp->_next != node2)
{
tmp = tmp->_next;
}
prenode2 = tmp;
postnode1 = node1->_next;
postnode2 = node2->_next;
//交换节点
prenode1->_next = node2;
node2->_next = postnode1;
prenode2->_next = node1;
node1->_next = postnode2;
}
总结链表节点交换时候至少需要考虑的情况有(这里不进行必要的参数验证):
1) 链表头节点不参与任何交换;
2) 节点相同就不必进行交换;
3) 节点相邻的时候要特殊处理。
之所以对单链表的节点交换作了较多的描述,是因为单链表的节点交换作是链表排序中
的基本操作,具体将在相应的实现中描述。因此这里给出的交换节点函数传入的直接就是链表和两个节点,如果传入的是节点的关键字,先遍历链表定位就可以了,相关数据结构的定义参见《单链表操作——交换节点》。