指针的指针应用||数据结构

一、概述

本篇文章主要通过单项链表的有序排序、链式搜索二叉树来探索C的能力即“C能够取得现存对象的地址”,与指针的指针通过间接操作符“*”改变指针所指向的指针的能力如出一辙。因此在读本篇文章之前需要基础的指针、链表、二叉树知识,当然接下来也会简单介绍指针的指针。

指针作为C这门高级语言的重要组成部分,因其操作内存的高风险与高效率引得众人心向往之,为此该篇文章着重突出“改变地址指向的内容”以解决复杂的数据结构问题。

二、指针的指针

首先我们声明一个整型变量:

int a=10;

对于以下表达式我们是否能清晰的明白含义:

*&a=20;

而a最终的结果是10还是20?

以上的表达式首先看左值*&a的含义,根据优先级先对a取地址,在通过间接操作符指向地址的内容,即左值是变量a本身,最后将右值赋值给左值a,结果与以下表达式一样:

a=20;

因此a最终结果为20。

倘若我们再声明一个指针变量指向整型变量a:

int a=10;

int* b;

b=&a;

或者再次声明一个变量:

int**p;

p=&b;

那么*b=20或者**p=20都可以达到a=20的目的。

由于举例颇为简单,只是介绍指针的指针的用法,并不能感受“C能够取得现存对象的地址”这个强大能力的魅力,为此接下来从复杂问题里探索这个能力的奥秘。

三、单项链表


图3.1

内存中存在着多个相同的结构类型且相关联的数据,构成一个集合。把每个独立的结构数据称之为节点,每个节点又包含一个指向下一个节点的指针,以及其他必要的数据。如图1所示,我们声明一个结构类型如下:

typedef struct  TABLENODE {

int value;

struct  TABLENODE* next;

}TableNode;

其中,整形变量value是节点的内容,next指向下一个节点。

如图1所示,当查找value值为11的节点,需要通过根节点找到第一个节点,再根据第一个节点的next指针寻找到第二个节点,依次并寻找到11这个节点。要说明的是根节点是指向第一个节点,并不不包含其他数据。另外我们图上有序的逻辑排列方便于直观探讨,而在实际物理内存中,不同节点占有的内存并不是连续的。

简单介绍完单项链表的含义,于是,可以引出如下思考:

如何将图1中的value值为9的节点删除,且不影响链表的完整性?

分析:

根据单项链表的不可逆性,从根节点依次查找节点的顺序是唯一的,通过比较当前节点的值与9的大小,如果不相等,则继续查找下一个节点,这也是循环判断的条件。

重要的是当查找到值为9这个当前节点时,接下来要进行的操作是提取当前节点指向的下一个节点11,并且要满足当前节点的前一个节点7的next指针指向11这个节点,最后删除9这个节点。

因此需要保存当前指针的前一个节点的指针。

步骤如下:

图3.2

第一步:如图3.2,查找第一个节点时,当前指针current指向第一个value值为3的节点,即current->value=3,与我们要删除的9是不等的,因此在current指针继续指向下一个节点之前,将current保存到previous。

图3.,3

第二步:如图3.3,查找第二个节点时,当前指针current指向第2个value值为7的节点,即current->value=7,与我们要删除的9是不等的,因此在current指针继续指向下一个节点之前,将current保存到previous。

图3.4

第三步:如图3.4,查找第3个节点时,当前指针current指向第3个value值为9的节点,即current->value=9,发现与我们要删除的9是相等的,此时只需要将previous节点的next指针指向current节点指向的下一个节点。得到图3.5所示。

图3.5

第一次尝试Delete函数如下:

void Delete(TableNode* node,int value)

{

    TableNode*current;

    TableNode*previous;

    previous=NULL;

    current= node;

    while((current!=NULL)&&(current->value!=value))

    {

        previous=current;

        current=current->Next;

    }

    previous->Next=current->Next;

    free(current);//销毁内存

}

第一次尝试删除函数存在如下问题:

1.传递的形参为根节点,如果需要删除第一个节点,就需要改变根节点的指向,由于形参传递不能改变形参本身的值,所以以上代码未考虑到这种情况。针对这种情况,我们只需要根据“函数的形参为指针时,可以改变指针指向的内容”这一特点,传递参数由根指针转换为根指针的地址。

2.传递的根节点有可能是空的,会导致其他问题。

对以上代码做如下改进:

void Delete(TableNode** node,int value)

{

    assert(node!=NULL);

    TableNode*current;

    TableNode*previous;

    previous=NULL;

    current= *node;

    while((current!=NULL)&&(current->value!=value))

    {

        previous=current;

        current=current->Next;

    }

    assert(current!=NULL);

    if(previous==NULL)

    {

        *node=current->Next;

    }

    else

    {

        previous->Next=current->Next;

    }

        free(current);

}

第二次尝试是可以达到正常解决问题的目的,但这并不是这篇文章的主旨,也未突出“指针的指针通过间接操作符*改变指针指向的指针”这一思想。

我们将根指针的地址即二维指针作为形参传递到函数,用一个一维指针current指向当前节点。当第一个节点不是删除对象时,我们只需使用二维指针保留current->next的地址,这个时候二维指针的意义变为指向当前节点的next指针。作为循环条件,在下次循环之前,重新将二维指针指向current,这样当前节点current指针自动前往下一个节,作为循环的条件进行判断。

因此我们只需要“指向当前节点的指针”以及“指向当前节点的next指针的指针”。

步骤如下:

图3.6

第一步:如图3.6,p为根节点的地址,此时*p指向第一个value为3的节点,与current等价。

图3.7

第二步:如图3.7,可以注意到p指向的是第一个节点的next指针位置,而不是第一个节点,这个至关重要。此时在下次循环之前p指向的内容保留到current指针,自动使得current指向value为7的节点。

图3.8

第三步:如图3.8,p指向的是第二个节点的next指针位置。此时在下次循环之前p指向的内容保留到current指针,自动使得current指向value为9的节点。

图3.9

第四步:如图3.9,判断当前的节点是需要删除的节点。将p指向当前节点current的next指针。

代码如下:

void Delete(TableNode** node,int value)

{

    assert(node!=NULL);

    TableNode*current;

    TableNode**p=node;

    current= *p;

    while((current!=NULL)&&(current->value!=value))

    {

        p=&current->Next;

        current=*p;

    }

    assert(current!=NULL);

    *p=current->next;

}

第三次尝试可以看到,并没有采取第二次尝试中的保留前一个节点的方式,而是抓住“指针的指针通过间接操作符*改变指针指向的指针”这一思想解决问题。而这类思想在链式搜索二叉树中也有所使用。

事情并没有结束,我们很快知道插入一个节点也可以这么操作,可以参照删除的思想,只贴上代码:

void insert(TableNode** node,int value)

{

    assert(node!=NULL);

    TableNode*current;

    TableNode**p=node;

    TableNode*insertnode;

    current= *p;

    while((current!=NULL)&&(current->value<value))

    {

        p=&current->Next;

        current=*p;

    }

    //新增:

    insertnode=(TableNode*)malloc(sizeof(TableNode));

    assert(insertnode!=NULL);

    insertnode->value=value;

    insertnode->next=current;

    *p=insertnode;

}

四、链式搜索二叉树

图4.1

二叉树的特点:当前节点的值要大于它左子树所有的节点值,并小于它右子树所有的节点值。

如上图4.1是一个二叉树,基于以上特点,我们可以采用链表的搜索方式查找二叉树,并将“指针的指针通过间接操作符*改变指针指向的指针”这一思想运用到其中。

例:在上图二叉树插入11.

结果如图4.2所示:

图4.2

节点结构为:

typedef struct  TREENODE {

int value;

struct  TABLENODE*left;

struct  TABLENODE*right;

}TreeNode;

代码如下:

void insert(TreeNode** node,int value)

{

    assert(node!=NULL);//

    TreeNode*current;//当前节点

    TreeNode*insertnode;//要插入的节点

    current= *node;

    while((current!=NULL))

    {

        if(current->value>value)

        {

            node=&current->left;

            current=*node;

        }

        else

        {

            if(current->value<value)

            {

                node=&currentt->right;

                current=*node;

            }

        }

    }

//新增:

    insertnode=(TreeNode*)malloc(sizeof(TreeNode));

    insertnode->left=NULL;

    insertnode->right=NULL;

    insertnode->value=value;

    *node=insertnode;

}

参考书籍:《C和指针》《C专家编程》《STL源码剖析》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容