链表翻转

问题
链表翻转。给出一个链表和一个数k,比如,链表为1→2→3→4→5→6,k=2,则翻转后2→1→6→5→4→3,若k=3,翻转后3→2→1→6→5→4,若k=4,翻转后4→3→2→1→6→5,用程序实现。
在之前我们肯定都写过链表的翻转程序:

NodePtr ReverseList(NodePtr head){
cout << "reverse list" << endl;
//若只有头或者只有一个节点,则不用翻转
if(head->next == NULL || head->next->next == NULL)
    return head;
NodePtr p, q, r;
p = head->next;
q = p->next;
//翻转过程
while(q){
    r = q->next;
    q->next = p;
    p = q;
    q = r;
} 
//翻转过后改变头指针
head->next = p;

return head;
} 

本题与全翻转链表有很大的相似之处,当然也有很大的不同之处。相似之处在于翻转的过程同事相同的,而不同之处则在于本题中包含两个部分的翻转,并且在头指针的指向以及第一个翻转过后还有一些操作需要处理。大致过程如下草图所示

该图中黑色表示链表的最初始状态,红色表示翻转的第一部分,绿色表示第二部分的翻转。
该实例表示的是k=2时的翻转,第一部分的翻转我们需要注意的问题是:

1.准确的定位。在链表中有时候会把人弄糊涂,因此一定要准确的定位到第二个节点,然后再开始翻转。

2.翻转过后头结点的指向。在本例中第一部分翻转过后一定要将head节点指向第一部分翻转的起始节点,即第二个节点。

3.第一部分翻转过后的最后一个节点的指向也一定要搞清楚。在本例中翻转过后第一个节点的下一个指向一定要指向节点3.

第一部分翻转完成过后我们开始第二部分的翻转。第二部分的翻转相对而言要简单很多,只需要注意翻转过后,一定要将第一部分翻转过后的最后一个节点的next指向最后一个节点。程序代码如下所示(这个程序还是有一点问题的,如果是全翻转的话会有问题,但是这只是增加判断的问题,主要思路都是对的)

bool ReverseLink(LinkList link, int k){
//若只有头或者只有一个节点,则不用翻转
if(link->next == NULL || link->next->next == NULL)
    return false;
LinkList p, q, r, firstNode;
int count = 1;
firstNode = link->next;
p = link->next;     //头结点 
q = p->next;        //第二个节点
//前k个节点的翻转 
while(count<k && q){
    r = q->next;
    q->next= p;
    p = q;
    q = r;
    count++;
} 
firstNode->next = r;
link->next = p;
//后面节点的翻转
p = firstNode->next;
q = p->next; 
while(q){
    r = q->next;
    q->next = p;
    p = q;
    q = r;
}
firstNode->next->next = NULL;
firstNode->next = p;

return true;    
}

下面是整体的代码,可以直接运行,已经在机器上测试过:

#include<iostream>
using namespace std;

typedef int ElemType;

struct Link{
    ElemType data;
    struct Link *next;
};

typedef Link *LinkList;

bool InitLink(LinkList *link){
    *link = (LinkList)malloc(sizeof(Link));
    if(!(*link))
        return false;
    (*link)->next = NULL;
    return true;
}

bool InsertList(LinkList *link, ElemType elem){
    if(NULL == *link)
        return false;
    LinkList p, q;
    p = *link;
    q = (LinkList)malloc(sizeof(Link));

    q->data = elem;
    q->next = NULL;

    while(NULL != p->next)
        p = p->next;
    p->next = q;

    return true;
}

bool ReverseLink(LinkList link, int k){
    //若只有头或者只有一个节点,则不用翻转
    if(link->next == NULL || link->next->next == NULL)
        return false;
    LinkList p, q, r, firstNode;
    int count = 1;
    firstNode = link->next;
    p = link->next;     //头结点 
    q = p->next;        //第二个节点
    //前k个节点的翻转 
    while(count<k && q){
        r = q->next;
        q->next= p;
        p = q;
        q = r;
        count++;
    } 
    firstNode->next = r;
    link->next = p;
    //后面节点的翻转
    p = firstNode->next;
    q = p->next; 
    while(q){
        r = q->next;
        q->next = p;
        p = q;
        q = r;
    }
    firstNode->next->next = NULL;
    firstNode->next = p;

    return true;    
}

void printLink(LinkList link){
    LinkList p;
    p = link;
    while(NULL != p->next ){
        cout << p->next->data << "---> " ;
        p = p->next;
    }

    cout << endl;
}

int main(){
    LinkList link;
    InitLink(&link);
    InsertList(&link, 1);
    InsertList(&link, 2);
    InsertList(&link, 3);
    InsertList(&link, 4);
    InsertList(&link, 5);
    InsertList(&link, 6);
    printLink(link);
    ReverseLink(link, 5);
    printLink(link);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值