24. Swap Nodes in Pairs

问题描述:
给定一个单向链表,依次交换相邻的两个节点,
要求:
不能更改节点元素值,使用常数级存储空间
 
问题分析:
如何交换两个元素?对于数组来说,我们可以采取辅助变量的方法进行交换元素,这种交换是将对应位置的元素进行交换。对于链表来说可以不用对元素进行操作,而是只是更改各个节点的指向关系,从而达到交换元素的目的。对于每一对相邻的节点来说,需要进行一下及步骤(a\b都是指针变量):
step1: 将第一个的元素a的后继更新为第二个元素b的后继:a->next = b->next;
step2:将第二个元素b的后继更新成为a:b->next = a;
step3:完成以上两个步骤后,该对元素的位置已经更改,但是还需要用完成调换顺序的元素中的第一个也就是原来的b来更新a指针的内容,如果不更新那么在链表读取首元素时,该指针指向的将会是第二个元素,直接将第一个元素丢弃,对于整个链表来说在奇数位置的节点都将无法访问。
 
具体代码实现如下:
 
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode** pp , *a, *b;
        pp = &(head);
        while( (a = *pp) && (b = a->next) ){
            a->next = b->next;
            b->next = a;
            *pp = b; //在这里利用*pp更新原有第一个指针的指向
            pp = &(a->next); //切换到下一组进行比较
        }
        return head;
    }
};
 
代码分析:
在这段代码中,我们使用ListNode** 变量来存储我们要更新的那个指针变量,为什么要使用指向指针的的变量,首先第一点我一直没有意识到链表访问我们一直使用指针来访问,为什么?我是着呢认为的,如果我们采用ListNode类型变量,那么我们在对其前驱和后继进行赋值时,就必须频繁使用取地址操作符,我们我们在节点内部的元素中包含了一个指向下一个元素的指针next,关于这里为什么要用指针类型来存储下一个节点的信息,在清华大学邓俊辉老师的数据结构中曾经讲到过,一个节点无法包含对自身的类型的元素,这个问题可以这么去想:如果允许的话,那么这种定义方式必然就陷入了无限循环的恶性模式,在初始化这样的一个节点的时候,哎不能想了,太恐怖了!所以我们都采用了指针的类型,这样就合乎逻辑了。指针只是一种类型的变量,其本身也就是4个字节。因此我们在操作指针时并没有对元素本身产生影响,只是对指针变量的指向产生了效果。
明确了以上,其实我还是不是很明确,只是知道这个概念,懂得概念和懂得去用这个差距还是很大的。
我们需要在每一次循环中更新某个指针变量的值,因此我们就必须要定义一个可以存储该类型的变量,那就是指向指针的指针了,也就是本代码中的pp变量。就像我们在步骤中所说一样,程序第一步,首先使得指针变量指向开始的两个元素,也就是a = head; b= head->next;这样讲a、b指针变量指向了第一个节点和第二个节点;然后对a、b指向的节点之间的前驱后继关系进行更改,a->next = b->next; b->next = a;指针变量更改完毕后我们还要将原来指向第一个元素的将其更新为可以指向交换次序后的第一个;最后更新要交换次序的元素。
 
我的问题:
我对此题产生的解的错误:
1、没有更新交换完元素的第一个元素的指向关系,我们最终将会返回head指针变量,因此就必须更新该变量的指向,否则还是只能指向原来的第一个元素,比如有链表1-》2-》3-》4-》5
更新完成之后对head变量的值并没有影响,head还是保存了节点1的地址,这样我们进行下去链表就变成1-》3-》5,那么本应在第一位置的元素丢失掉了。
2、就是对指针的用法不熟,更新指针变量并不能更新节点本身,这个我一直陷入了错误的想法中,以为更新指针变量就是更新了相应的节点比如说ListNode* a = head ,a = b;那这两个表达式产生了什么效果,将a指针变量更新成为head指针变量的值,那么对a进行*访问,就可以访问到原来head指向的那个节点,然后又将a的值更新为指针变量b的值,然后可以通过对a进行*动作,就可以取到原来b可以指向的节点,此时如果a不在更新值,那么a、b都可以访问同一个节点。
 
那么如果是双向链表呢,怎么做?数组呢?
 
数组版本代码:
 
class swappairs
{
public:
    int*  methodOfSwap(int *a, int l){
        int i = 0;
        for(; i < l - 1; i += 2){
            int b;
            b = a[i];
            a[i] = a[i+1];
            a[i+1] = b;
        }
        return a;
    }
 
};
 
数据版本比较简单
 
双链表版本:
 
双链表相对于单链表来说,还需要同时更新节点的前去和后继就是,还有下一个交换的节点的前继,代码如下:
 
class doubleSolution {
public :
    ListNode* swappair( ListNode *head ) {
        ListNode ** nptr, *a, *b;
        nptr = & head ;
        while ((a = *nptr) && (b = a->next)) {
            a->next = b->next;
            b->next = a;
            b->prev = a->prev;
            a->prev = b;
            if (a->next != nullptr )
                a->next->prev = a;
 
            *nptr = b;
            nptr = &(a->next);
        }
        return head ;
    }
};
 
以上代码亲测可行
 
posted @ 2016-09-05 14:50  罐装可乐  阅读(228)  评论(0编辑  收藏  举报