数据结构——使用栈判定回文数_用栈实现回文判断的算法-程序员宅基地

技术标签: 数据结构与算法  数据结构  

1.回文数定义与题目解读

回文数是指正读反读都能读通的句子。比如“我为人人,人人为我”、“1234321”、“abcba”是回文数,但“一杯茶一包烟,一行代码写一天”,“123456”,“abcdef”就不是回文数。
那么通过以上的几个例子可以发现以下几点:

  1. 回文数是要求这串文字的左侧与右侧相同,即回文数左右对称,但是若字符个数为奇数的情况下,那么中间的字符并不会对整个判断其影响作用,所以由此可以推测出回文数需要考虑整个文字的奇偶性。
  2. 由于是比较第i个字符与第n-i-1个字符(这里是以数组游标来计算的i,即i从0开始),那么用栈解决无疑是最合适的方式。

2.栈的构建

使用栈那么一共需要栈的init(), push(), pop()方法,笔者在这使用的是链式栈来解决问题,读者有兴趣也可以考虑使用顺序栈实现。
考虑到可能会有数据结构的初学者,所以这里简单讲解下链栈的工作模式:

  1. 栈是只允许在一端进行插入或删除的线性表,栈的一个明显的操作特性是先进后出(first in last out, FILO)。
  2. 链栈的push()方法就是在栈的顶部对该单链表进行前插操作,如图所示:
    链栈的入栈操作
  3. 链栈的pop()方法就是删除top所指向的结点,如图所示:
    链栈的出栈操作
    相应链栈的构建代码如下所示:
typedef struct SNode {
    
    char data;  // 数据域
    struct SNode *next;  // 指针域
}SNode, *SLink;

typedef struct LinkStack {
    
    SLink top;  // 栈顶指针
    int count;  // 栈中结点个数
}LinkStack;

/**
 * 初始化链式栈
 * param: S(需要操作的链栈)
 */ 
void initStack(LinkStack *&S) {
    
    S = (LinkStack *)malloc(sizeof(LinkStack));
    S->top = (SNode *)malloc(sizeof(SNode));
    
    S->top->next = NULL;
    S->top = NULL;
    S->count = 0;
}

/**
 * 向栈中加入数据
 * param: *S(需要操作的栈), elem(向栈中添加的元素)
 * return: true(操作成功)
 *         false(操作失败)
 */ 
bool push(LinkStack *S, char elem) {
    
    SLink node = (SLink)malloc(sizeof(SNode));  // 创建新结点
    node->data = elem;
    node->next = S->top;
    S->top = node;
    S->count++;
    return true;
}

/**
 * 弹出栈顶元素
 * param: *S(需要操作的栈), &elem(弹出的第一个元素)
 * return: true(操作成功)
 *         false(栈为空)
 */ 
bool pop(LinkStack *S, char &elem) {
    
    
    //空栈
    if(S->top == NULL) {
    
        return false;
    }

    elem = S->top->data;
    SNode *node = S->top;  // 工作指针
    S->top = S->top->next;
    free(node);
    S->count--;
    return true;
}

3.判断回文数方法

3.1 数组的输入

之前在第一部分的时候提到了,回文数判断的核心在于判断该数组的奇偶性,那么对整个数组的长度的把握至关重要。由于C/C++不像python一样有内置的len()函数,所以在计算数组长度的时候有些许麻烦。笔者在编码过程中是手动键入数组每个数的,所以通过判断输入的次数来记录长度。若读者是采用直接ElemType arr[] = {}这种方式创建数组,则可以采用int len = sizeof(arr) / sizeof(arr[0]);这种方式计算。

/**
 * 键入数组, 键入*或者一共键入MaxSize个字符停止
 * param: &arr[](需要操作的数组)
 * return: length(数组长度)
 */ 
int enter_array(char arr[]) {
    
    char c;
    int length = 0;
    for (int i = 0; i < MaxSize; i++) {
    
        cin>>c;
        if (c == '*') {
    
            break;
        }
        arr[i] = c;
        length++;
    }
    return length;
}

3.2 回文数方法

前面已经完成了相应的铺垫工作,现在开始进入正题。
首先,最开始提到过,回文数是判断一个数组的左右是否对称,那么我们只需要考虑将数组前半段压入栈中,即从int i = 0循环到i < length / 2。
有的读者可能会有疑问,在C/C++中整型相除若产生浮点数,那么会省略结果的小数点之后的值,即向下取整,为什么这里还使用length / 2?因为之前也提到过,一串奇数个字符是否为回文数,与其正中间的字符毫无关系,所以这里可以大胆的使用i < length / 2。

// 将数组前半段入栈, 若length为奇数, 则向下取整
for (int i = 0; i < length / 2; i++) {
    
    push(S, arr[i]);
}

接着,就是我们要考虑数组的奇偶性的时候了,因为我们需要遍历整个数组的后半部分的数据,那么开始的起始位置就至关重要。
当字符数为奇数个时,起始位置为length / 2 + 1,当字符数为偶数个时,起始位置为length / 2
至于为何为这两个值,笔者画了个图进行说明,红色表明的部分为数组应该开始遍历的起始位置。
数组起始位置讲解

// 判断数组奇偶性
int start;  // 用于记录数组中的字符与栈中字符比较的起始位置
    
// 若数组为偶数
if (length % 2 == 0) {
    
        start = length / 2;
}
// 若数组为奇数
else {
    
    start = length / 2 + 1;
}

最后就只剩下判断数组遍历的字符与出栈的字符是否相等,若不相等,则不是回文数,若整个程序活到了遍历结束,那么就是回文数。

for (int i = start; i < length; i++) {
    
    char c;
    pop(S, c);
        
    // 若出栈的字符与arr[i]不等
    if (c != arr[i]) {
    
       return false;
    }
}
    
return true;

4.算法分析

  1. 时间复杂度:整个算法虽然使用了两个for循环,但是分别遍历了数组的左右两侧,所以时间复杂度为O(n)
  2. 空间复杂度:整个算法使用了栈这种数据结构与一个数组,而栈只存入了至多一半的数组长度,所以空间复杂度为O(n + n / 2) = O(3n / 2)

5.整体代码与结果截图

#include <iostream>
using namespace std;

#define MaxSize 20

typedef struct SNode {
    
    char data;  // 数据域
    struct SNode *next;  // 指针域
}SNode, *SLink;

typedef struct LinkStack {
    
    SLink top;  // 栈顶指针
    int count;  // 栈中结点个数
}LinkStack;

/**
 * 初始化链式栈
 * param: S(需要操作的链栈)
 */ 
void initStack(LinkStack *&S) {
    
    S = (LinkStack *)malloc(sizeof(LinkStack));
    S->top = (SNode *)malloc(sizeof(SNode));
    
    S->top->next = NULL;
    S->top = NULL;
    S->count = 0;
}

/**
 * 向栈中加入数据
 * param: *S(需要操作的栈), elem(向栈中添加的元素)
 * return: true(操作成功)
 *         false(操作失败)
 */ 
bool push(LinkStack *S, char elem) {
    
    SLink node = (SLink)malloc(sizeof(SNode));  // 创建新结点
    node->data = elem;
    node->next = S->top;
    S->top = node;
    S->count++;
    return true;
}

/**
 * 弹出栈顶元素
 * param: *S(需要操作的栈), &elem(弹出的第一个元素)
 * return: true(操作成功)
 *         false(栈为空)
 */ 
bool pop(LinkStack *S, char &elem) {
    
    
    //空栈
    if(S->top == NULL) {
    
        return false;
    }

    elem = S->top->data;
    SNode *node = S->top;  // 工作指针
    S->top = S->top->next;
    free(node);
    S->count--;
    return true;
}

/**
 * 查看栈中元素
 * param: *S(需要操作的栈)
 */ 
void visit(LinkStack *S) {
    
    SLink node = S->top;
    while (node != NULL) {
    
        cout<<node->data<<"  ";
        node = node->next;
    }
    cout<<endl;
}

/**
 * 键入数组, 键入*或者一共键入MaxSize个字符停止
 * param: &arr[](需要操作的数组)
 * return: length(数组长度)
 */ 
int enter_array(char arr[]) {
    
    char c;
    int length = 0;
    for (int i = 0; i < MaxSize; i++) {
    
        cin>>c;
        if (c == '*') {
    
            break;
        }
        arr[i] = c;
        length++;
    }
    return length;
}

/**
 * 回文数判断
 * param: &S(需要操作的栈), arr[](需要判断的数组), length(数组长度)
 * return: true(是回文数), false(不是回文数)
 */ 
bool is_palindrome_number(LinkStack *&S, char arr[], int length) {
    
    initStack(S);

    // 将数组前半段入栈, 若length为奇数, 则向下取整
    for (int i = 0; i < length / 2; i++) {
    
        push(S, arr[i]);
    }

    // 判断数组奇偶性
    int start;  // 用于记录数组中的字符与栈中字符比较的起始位置
    
    // 若数组为偶数
    if (length % 2 == 0) {
    
        start = length / 2;
    }
    // 若数组为奇数
    else {
    
        start = length / 2 + 1;
    }

    // 回文数判断
    for (int i = start; i < length; i++) {
    
        char c;
        pop(S, c);
        
        // 若出栈的字符与arr[i]不等
        if (c != arr[i]) {
    
            return false;
        }
    }
    
    return true;
}

int main() {
    
    LinkStack *S;
    char arr[MaxSize];
    int length = enter_array(arr);
    
    if(is_palindrome_number(S, arr, length)) {
    
        cout<<"this array is a palindrome number"<<endl;
    } else {
    
        cout<<"this array is not a palindrome number"<<endl;
    }

    system("pause");
    return 0;
}

数组是回文数截图
素组不是回文数截图

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_35357274/article/details/103774098

智能推荐

视频教程-微信小程序支付java-Java-程序员宅基地

文章浏览阅读354次。微信小程序支付java 我是一名java高级开发工程师,已有七年的工作经验,..._微信小程序支付教学视频

HarmonyOS Next从入门到精通实战精品课

Canvas完成矩形的绘制;华为闹钟的订阅和取消;华为闹钟的基本绘制;华为闹钟的时针-分针-秒针的绘制;华为闹钟的任务列表的样式;关于State的状态更新的须知;关于样式的简单介绍;关于vp和fp的介绍;知乎数据的真实的渲染;实现底部组件的封装;华为闹钟的添加闹钟;完成知乎小案例的UI布局;ForEach的商品列表案例;ForEach的商品列表的Grid布局;ForEach的key的一个简单介绍;Watch的刷题案例-做题的思路;State嵌套更新的实现方式;你画我猜的签字板实现;

vue2实现复制粘贴功能

【代码】vue2实现复制粘贴功能。

SpringBoot引入Layui样式总是出现404

本文主要介绍的是在项目开发中SrpingBoot引入Layui样式出现404的解决方案

STM32与Proteus的串口仿真详细教程与源程序

解决了STM32的Proteus串口收发问题。

深入理解C语言中的 extern`和 static

extern关键字用于声明一个变量或函数,表示其定义在另一个文件或本文件的其他位置。使用extern可以在多个文件之间共享全局变量或函数。static关键字用于声明变量或函数的作用域为仅限于定义它们的文件,同时保持它们的值在函数调用之间持久存在。理解并正确使用extern和static关键字对于管理大型C语言项目中的变量和函数作用域、链接属性和生命周期至关重要。希望这篇文章能帮助你更好地掌握这些概念。

随便推点

xfce4 panel 不能显示QQ,钉钉的状态图标

ayatana的进程(/usr/libexec/ayatana-indicator-application/ayatana-indicator-application-service)连接着,从来没注意到这个包,感觉是它和xfce4-panel冲突了,杀掉这个进程后,这个dbus name也不见了,在shell中重新启用这个进程,这个dbus name又能出现,惊喜的时此时连接这个dbus的却又是xfce4 panel,于是QQ的图标就能在“状态栏插件”中显示了。保存后,重启系统即可。

使用 Docker 自建一款怀旧游戏之 - 扫雷

扫雷 是一种经典的单人电脑游戏,最初由微软公司在 1990 年代开发并内置在 Windows 操作系统中。游戏的目标是在一个由方块组成的网格上揭开所有非地雷的方块,而不触发地雷。每个方块上都标有数字,表示周围 8 个方块中包含的地雷数量。玩家需要根据这些数字来推断哪些方块是安全的,以便逐步揭开整个区域。尽管扫雷是一个简单的游戏,但它需要玩家运用逻辑推理和猜测的技巧,因此备受喜爱,并且已经成为了计算机游戏史上的经典之一。

VUE3与Uniapp 四 (Class变量和内联样式)

【代码】VUE3与Uniapp 三 (Class变量和内联样式)

前端打包过大如何解决?

前端开发完毕部署到线上是,执行npm run build。当打包过大时,部署到服务端后加载缓慢,如何优化?前端打包成gzip,用new CompressionWebpackPlugin来压缩。服务端nginx设置。我们可以通过执行npm run analyze。可以看到各个包文件大小的区别。当打包过大时,通过压缩gzip的方式,可以看个开始和打包和压缩的大小。

c语言上机入门实验十,C语言入门学习-C上机实验三要求-程序员宅基地

文章浏览阅读1.2k次。2+ x3- x4 +…… -x2n + x2n+1……的值。当某项的绝对值小于10-6时终止。(当x为0.5时,和值为0.333334)【系统函数fabs(x)的功能是计算x的绝对值,前面需加math . h头文件】3.一个球从100m高度自由落下,每次落地后反跳回原高度的一半,再落下,再反弹。编程计算:“它在第10次落地后,反弹多高”;“从第一次落下到第十次反弹,总共经过了多少米”。 (结果:..._入x,x>1,求以下数列之和,当某项绝对值小于10-6为止。

R语言实现PVAR(面板向量自回归模型)_pvar模型r语言-程序员宅基地

文章浏览阅读1.9w次,点赞48次,收藏173次。这次研究了一个问题,要用PVAR(面板向量自回归模型),在网上找的教程基本上都是Stata或者Eviews的教程,而鲜有R实现PVAR的教程,这里总结分享一下我摸索的PVAR用R实现的整个过程。..._pvar模型r语言

推荐文章

热门文章

相关标签