• 设计鲁棒性的方法:输入一个链表的头结点,逆序遍历打印该链表出来


    之前有过整理链表等的概念和基本算法。比较重要的是插入,删除,遍历,建表(尾插法,头插法)

    回忆链表尾部插入结点:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 typedef struct Node{
     5     int data;//数据域
     6     Node *next;//指针域
     7 } Node, *List;
     8 
     9 //在单链表的末位添加一个结点
    10 void addNode(List *head, int value)
    11 {
    12     //先动态的创建结点
    13     Node *newNode = new Node();
    14     newNode->data = value;
    15     newNode->next = NULL;
    16     //判断表是否为空,head为头指针
    17     if (*head == NULL) {
    18         *head = newNode;
    19         cout << (*head)->data << endl;
    20     }
    21     else{
    22         //不为空表,尾插,先遍历找到尾结点
    23         Node *p = *head;
    24         //从头到尾遍历单链表
    25         while (p->next != NULL) {
    26             //p 作为标记,顺次后移
    27             p = p->next;
    28         }
    29         //找到了尾结点,插入新结点
    30         p->next = newNode;
    31         cout << p->next->data << endl;
    32     }
    33 }
    34 
    35 int main(void)
    36 {
    37     List node = NULL;
    38     addNode(&node, 10);
    39     addNode(&node, 100);
    40     
    41     return 0;
    42 }

    要区分链表和顺序表(数组)之间的区别,顺序表(比如数组)可以随机存储,时间复杂度是 o(1),链表是离散的,动态的分配的,只能从头到尾遍历,不能随机存储,时间复杂的是 o(n),且注意空表的情况,还有二级指针的使用问题,注意到了这几点,一般没有问题了。

    删除链表中第一个寻找到的目标结点

     1 //查找到结点的 data 为val的第一个结点,然后删除之
     2 void deleteNode(List *head, int value)
     3 {
     4     //指示指针
     5     Node *p = *head;
     6     //指向待删除结点的指针
     7     Node *del = NULL;
     8     //先判断是否空表
     9     if (*head == NULL || head == NULL) {
    10         cout << "空表" << endl;
    11         exit(0);
    12     }
    13     //然后判断头结点
    14     if ((*head)->data == value) {
    15         del = *head;
    16         *head = (*head)->next;
    17     }
    18     //遍历寻找,注意遍历结束的标志有两个,没找到,找到
    19     while (p->next !=NULL && p->next->data != value) {
    20         p = p->next;
    21     }
    22     //循环遍历结束,判断找到的情况
    23     if (p->next != NULL && p->next->data == value) {
    24         del = p->next;
    25         //删除
    26         p->next = p->next->next;
    27     }
    28     //销毁内存
    29     delete del;
    30     //消除野指针
    31     del = NULL;
    32 }
    33 
    34 void traversal(List head)
    35 {
    36     Node *p = head;
    37     if (p == NULL) {
    38         cout << "空表" << endl;
    39         exit(0);
    40     }
    41     
    42     while (p != NULL) {
    43         cout << p->data << endl;
    44         p = p->next;
    45     }
    46 }
    47 
    48 int main(void)
    49 {
    50     List node = NULL;
    51     //traversal(node);
    52     addNode(&node, 10);
    53     addNode(&node, 100);
    54     traversal(node);
    55     deleteNode(&node, 10);
    56     traversal(node);
    57     
    58     return 0;
    59 }

    10

    100

    10

    100

    100

    Program ended with exit code: 0

    输入一个链表的头结点,逆序遍历打印该链表

    链表的结点结构

    typedef struct Node{
        int data;//数据域
        Node *next;//指针域
    } Node, *List;

    直接的思路:改变链表的方向,从头到尾输出,也就是把链表的结点的指针反转,但是,这样会改变原单链表的结构,如果不可以改变链表的结构,那么这个方法就不可行。

    但是不论怎样,肯定是要遍历链表,只不过这里要求是逆序的遍历,也就是第一个找到的结点,让它最后一个输出。联系到了栈这个数据结构,先进后出。在遍历的时候,每查找到一个结点,就把这个结点压栈,遍历结束,出栈,就是逆序了。

    依靠c++ STL stack 实现逆序打印单链表

    stack不能遍历,所以没有迭代器,必须添加头文件 #include <stack>

     1 //依靠栈来实现逆序打印单链表
     2 //输入单链表的头结点,实现单链表的逆序打印
     3 void traversalReverse(List head)
     4 {
     5     //使用 c++ STL stack
     6     stack<List> nodes;
     7     //指示指针
     8     Node *p = head;
     9     //遍历
    10     while (p != NULL) {
    11         //入栈
    12         nodes.push(p);
    13         //指针后移
    14         p = p->next;
    15     }
    16     //遍历完毕,从栈中输出结点
    17     //empty()方法:堆栈为空则返回真
    18     while (!nodes.empty()) {
    19         //stack 没有迭代器,取出栈顶元素
    20         p = nodes.top();
    21         cout << p->data << " ";
    22         //出栈
    23         nodes.pop();
    24     }
    25 }
    26 
    27 int main(void)
    28 {
    29     List node = NULL;
    30     addNode(&node, 10);
    31     addNode(&node, 100);
    32     addNode(&node, 101);
    33     addNode(&node, 102);
    34     addNode(&node, 103);
    35     traversalReverse(node);
    36     
    37     return 0;
    38 }

    10

    100

    101

    102

    103

    103 102 101 100 10 Program ended with exit code: 0

    联系递归,递归在本质上就是一栈结构,还可以使用递归来直接实现逆序打印单链表

    在一次递归中,每次访问到一个结点,先打印该结点的后续一个结点,然后打印该结点本身,这样效果就是把链表逆序打印输出。

     1 void traversalRecursive(List head)
     2 {
     3     //先判断链表是否为空
     4     if (head != NULL) {
     5         //递归结束的条件
     6         if (head->next != NULL) {
     7             //先打印该结点的后续结点
     8             traversalRecursive(head->next);
     9         }
    10         //然后打印该结点
    11         cout << head->data << "	";
    12     }
    13 }
    14 
    15 int main(void)
    16 {
    17     List node = NULL;
    18     addNode(&node, 10);
    19     addNode(&node, 100);
    20     addNode(&node, 101);
    21     addNode(&node, 102);
    22     addNode(&node, 103);
    23     traversalRecursive(node);
    24     
    25     return 0;
    26 }

    递归的优点:代码简单明了

    递归的缺点:如果链表很长,导致递归调用层次很深,有可能导致函数的调用栈溢出,故一般第一个方法,显式的使用栈来实现逆序打印单链表的鲁棒性要好一些。

    何为代码的鲁棒性?

    鲁棒是Robust的音译,也就是健壮和强壮的意思。它是在异常和危险情况下系统生存的关键。比如说,计算机软件在输入错误、磁盘故障、网络过载或有意攻击情况下,能否不死机、不崩溃,就是该软件的鲁棒性。所谓“鲁棒性”,是指控制系统在一定(结构,大小)的参数影响下,维持其它某些性能的特性。

    欢迎关注

    dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

  • 相关阅读:
    第一课:数据库介绍篇
    爬虫day03 cast
    python excel
    爬虫 cast_day05_多进程_多线程_协程_selenium的使用
    进程_线程_协程回顾!
    进程_线程_协程回顾 2!
    day 06 爬虫
    看正月点灯笼老师的笔记 —动态规划2.1
    动态规划——放苹果

  • 原文地址:https://www.cnblogs.com/kubixuesheng/p/4322189.html
Copyright © 2020-2023  润新知