c++链表类模板问题(不要用c语言,用c++)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++链表类模板问题(不要用c语言,用c++)相关的知识,希望对你有一定的参考价值。

1.定义链表类模板实现
a) 降序冒泡排序;
b) 合并两个已降序排列的链表,合并的链表仍按降序排列。

2.试用栈实现带括号的四则混合运算(+、-、*、/)。运算表达式由键盘输入,如(1-(2+3*4))/5,按回车执行计算。
计算规则为
a. 先计算括号内,再计算括号外;
b. 无括号或同层括号内,先乘除,后加减,即乘除运算优先级高于加减计算;
c. 同一优先级运算,从左到右依次进行。

第一题很简单,是C++对链表的基本操作,要求必须熟练掌握!!希望楼主能通过书上的讲解和一些简单的实例自己好好研究一下。学习C++或者数据结构如果连这都不会的话就别谈其他高深的东西了。。。对自己要有信心!

第二题要难一些,这里就按楼主的要求说一说吧,希望对你有所启示。(双栈实现)
首先大致说一下后缀表达式(也叫逆波兰式):
后缀(postfix, 也成逆波兰 reverse Polish)表达式在我们的生活中并不常见,在我们日常中见到的,通常都是中缀(infix)式,例如:
3.14 + 15 * (9.2 – 6.5)
这是便于人类理解的表达式,之所以便于人类理解,是因为人从小便接受识别此类表达式的教育,而且这种记号方式将运算符和数字明确的分开,不会产生数字堆叠在一起的混乱情况。
但是对于计算机而言,这样的表达式并不好理解,计算机是一种线性读入信息,线性输出信息的工具,人类所通识的中缀式,对于这种规规矩矩按照顺序计算的工具而言,是不容易理解的。你可能一眼就看出来要先算小括号里的表达式,然后算乘法,最后算加法。而计算机直接读入的话,可能会先算3.14 + 15,这自然是荒谬的,而后缀法就为计算机计算表达式提供了一种非常有效的解决方案。这篇文章主要的内容是介绍如何将中缀表达式转换为后缀表达式。
说了这么半天,后缀表达式又是什么样子呢?它又有什么样的优势呢?
我们现在来看一组对比:
中缀式 后缀式
a + b a b +
a + b * c a b c * +
(a + b) * c a b + c *
后缀表达式为什么会有优势呢?因为计算机线性读入的特征,
我们以第二个表达式为例,以:
用后缀式,在计算机的计算过程就是:
a
a b
a b c
a b c *
a (b * c) 计算出b * c的值记作x
a x +
(a + x) 计算出a + x 的值
就是这样一个符合线性读入过程的运算,这样就合理的解决了运算之间优先关系的处理。
那么如何将一个中缀式装换为后缀式呢?
其实算法很简单,我们使用两个栈,一个是表达式栈,用来存储转换成后缀表达式的结果,一个是运算符栈,用来暂存表达式中的运算符,这个栈满足条件“从栈顶到栈底,运算符的优先级依次下降”,我们以表达式a + b * (c + d) 作为例子,来演示一下转换的过程:
1.读入数字a,存入表达式栈,紧接着读入运算符+,存入运算符栈
2.读入数字b,存入表达式栈,紧接着读入运算符*,由于*比+运算优先级高,所以也可以存入运算符栈
3.读入左括号(,(具有最高优先级,所以也存入运算符栈,后面的数字c存入表达式栈
4.读入运算符+,存入运算符栈,然后读入数字d,存入表达式栈
5.读入右括号),开始弹出运算符栈中的运算符到表达式栈,直到遇到左括号为止
6.表达式已经读完了,将运算符栈中的运算符全部弹出到表达式栈,至此后缀表达式已经转换完成!

总结下来,基本步骤很明确,就是以下转换的一般步骤:

(1)读入,如果是数字,就置入表达式栈,然后重复(1)。如果是运算符继续
(2)如果是’)’,则将运算符栈中直到’(‘之前的运算符都弹入表达式栈,并弹出’(‘。否则继续
(3)与运算符栈中的栈顶表达式比较,优先级高的话,置入运算符栈。否则将栈内的低优先级运算符弹入表达式栈,然后将新的运算符置入表达式栈。继续
(4)如果所有的字符都已经读入完成,则将运算符栈中的符号全部弹入表达式栈,至此完成转换。否则,回到步骤(1)。

转换完成以后,那么我们就可以进行计算了。计算方法依然比较容易:
1.从操作符栈POP出一个操作符。
2.从数据战中POP出两个数。
3.对这两个数执行1中POP出的操作符的对应运算,并将结果PUSH入数据栈中。
4.重复1—3,直到数据栈中只剩下一个数(或者操作符栈为空),那么数据栈中最后的元素便是运算结果!!!

以下为自己敲的代码:
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
#define Msize 256

/**********************定义栈模板*********************/
template<typename elemtype>//定义栈的类模板
class Stack
typedef struct Node
elemtype data;
struct Node *pro, *next;
Node;
private:
Node *base;
Node *top;
public:
void Init_Stack();
void Push_Stack(elemtype e);
void Pop_Stack(elemtype &e);
elemtype Gettop();
bool Istempt();
;

template<typename elemtype>
void Stack<elemtype>::Init_Stack()//初始化

base = (Node*)malloc(sizeof(Node));
if(NULL==base)
cout<<"申请存储空间失败!!";
exit(0);

top = NULL;
base->pro = NULL;
base->next = NULL;


template<typename elemtype>
void Stack<elemtype>::Push_Stack(elemtype e)//入栈

Node *s;
if(NULL==top)
top = base;
top->data = e;

else
s = (Node*)malloc(sizeof(Node));
if(NULL==s)
cout<<"申请空间失败!!";
exit(0);

s->next = NULL;
s->data = e;
s->pro = top;
top->next = s;
top = s;



template<typename elemtype>
void Stack<elemtype>::Pop_Stack(elemtype &e)//出栈

Node *p;
p = top;
if(NULL==top)
cout<<"栈已空!出栈失败!!"<<endl;
return;

e = top->data;
top = top->pro;
if(NULL!=top)
free(p);



template<typename elemtype>
elemtype Stack<elemtype>::Gettop()//取栈顶元素

if(NULL==top)
return EOF;

else
return top->data;



template<typename elemtype>
bool Stack<elemtype>::Istempt()//判断栈是否为空

if(NULL==top)
return true;

else
return false;



/****************************主函数****************************/
int main()

Stack<float> data;
Stack<int> op;
char str[Msize], ctemp[10], *q;
int ntemp, range, flag = 0, i;
float add1, add2, nresult;
op.Init_Stack();
data.Init_Stack();
cout<<"输入正确的表达式::"<<endl;
cin>>str;
for(i=0;i<strlen(str);i++) //逆波兰运算
if(str[i]>='0'&&str[i]<='9')
q = ctemp;
while(str[i]>='0'&&str[i]<='9')
*q = str[i];
i++;
q++;

*q = '\0';
ntemp = atoi(ctemp);
data.Push_Stack(ntemp);
if(i>=strlen(str))
break;


if(str[i]=='(')
flag = flag + 4;
continue;

if(str[i]==')')
flag = flag - 4;
continue;

switch(str[i])
case '+': range = 1 + flag; break;
case '-': range = 2 + flag; break;
case '*': range = 3 + flag; break;
case '/': range = 4 + flag; break;
default : break;

if(op.Istempt())
op.Push_Stack(range);

else
while(range<op.Gettop())
data.Pop_Stack(add1);
data.Pop_Stack(add2);
op.Pop_Stack(ntemp);
switch(ntemp%4)
case 1: data.Push_Stack(add2+add1); break;
case 2: data.Push_Stack(add2-add1); break;
case 3: data.Push_Stack(add2*add1); break;
case 0:

if(0==add1)
cout<<"除数为0,错误!!!"<<endl;
exit(0);

data.Push_Stack(add2/add1); break;

default : break;


op.Push_Stack(range);


while(!op.Istempt())
data.Pop_Stack(add1);
data.Pop_Stack(add2);
op.Pop_Stack(ntemp);
switch(ntemp%4)
case 1: data.Push_Stack(add2+add1); break;
case 2: data.Push_Stack(add2-add1); break;
case 3: data.Push_Stack(add2*add1); break;
case 0:

if(0==add1)
cout<<"除数为0,错误!!!"<<endl;
exit(0);

data.Push_Stack(add2/add1); break;

default : break;


data.Pop_Stack(nresult);
cout<<endl<<"计算结果为::"<<endl;
cout<<str<<" = "<<nresult<<endl;
return 0;

以上代码不含表达式错误检测,计算时必须输入正确的算式!!!(纯手工制作,供楼主参考!望高手指正!!)
参考技术A 第一题很简单,是C++对链表的基本操作,要求必须熟练掌握!!希望楼主能通过书上的讲解和一些简单的实例自己好好研究一下。学习C++或者数据结构如果连这都不会的话就别谈其他高深的东西了。。。对自己要有信心!
第二题要难一些,这里就按楼主的要求说一说吧,希望对你有所启示。(双栈实现)
首先大致说一下后缀表达式(也叫逆波兰式):
后缀(postfix,
也成逆波兰
reverse
Polish)表达式在我们的生活中并不常见,在我们日常中见到的,通常都是中缀(infix)式,例如:
3.14
+
15
*
(9.2

6.5)
这是便于人类理解的表达式,之所以便于人类理解,是因为人从小便接受识别此类表达式的教育,而且这种记号方式将运算符和数字明确的分开,不会产生数字堆叠在一起的混乱情况。
但是对于计算机而言,这样的表达式并不好理解,计算机是一种线性读入信息,线性输出信息的工具,人类所通识的中缀式,对于这种规规矩矩按照顺序计算的工具而言,是不容易理解的。你可能一眼就看出来要先算小括号里的表达式,然后算乘法,最后算加法。而计算机直接读入的话,可能会先算3.14
+
15,这自然是荒谬的,而后缀法就为计算机计算表达式提供了一种非常有效的解决方案。这篇文章主要的内容是介绍如何将中缀表达式转换为后缀表达式。
说了这么半天,后缀表达式又是什么样子呢?它又有什么样的优势呢?
我们现在来看一组对比:
中缀式
后缀式
a
+
b
a
b
+
a
+
b
*
c
a
b
c
*
+
(a
+
b)
*
c
a
b
+
c
*
后缀表达式为什么会有优势呢?因为计算机线性读入的特征,
我们以第二个表达式为例,以:
用后缀式,在计算机的计算过程就是:
a
a
b
a
b
c
a
b
c
*
a
(b
*
c)
计算出b
*
c的值记作x
a
x
+
(a
+
x)
计算出a
+
x
的值
就是这样一个符合线性读入过程的运算,这样就合理的解决了运算之间优先关系的处理。
那么如何将一个中缀式装换为后缀式呢?
其实算法很简单,我们使用两个栈,一个是表达式栈,用来存储转换成后缀表达式的结果,一个是运算符栈,用来暂存表达式中的运算符,这个栈满足条件“从栈顶到栈底,运算符的优先级依次下降”,我们以表达式a
+
b
*
(c
+
d)
作为例子,来演示一下转换的过程:
1.读入数字a,存入表达式栈,紧接着读入运算符+,存入运算符栈
2.读入数字b,存入表达式栈,紧接着读入运算符*,由于*比+运算优先级高,所以也可以存入运算符栈
3.读入左括号(,(具有最高优先级,所以也存入运算符栈,后面的数字c存入表达式栈
4.读入运算符+,存入运算符栈,然后读入数字d,存入表达式栈
5.读入右括号),开始弹出运算符栈中的运算符到表达式栈,直到遇到左括号为止
6.表达式已经读完了,将运算符栈中的运算符全部弹出到表达式栈,至此后缀表达式已经转换完成!
总结下来,基本步骤很明确,就是以下转换的一般步骤:
(1)读入,如果是数字,就置入表达式栈,然后重复(1)。如果是运算符继续
(2)如果是’)’,则将运算符栈中直到’(‘之前的运算符都弹入表达式栈,并弹出’(‘。否则继续
(3)与运算符栈中的栈顶表达式比较,优先级高的话,置入运算符栈。否则将栈内的低优先级运算符弹入表达式栈,然后将新的运算符置入表达式栈。继续
(4)如果所有的字符都已经读入完成,则将运算符栈中的符号全部弹入表达式栈,至此完成转换。否则,回到步骤(1)。
转换完成以后,那么我们就可以进行计算了。计算方法依然比较容易:
1.从操作符栈POP出一个操作符。
2.从数据战中POP出两个数。
3.对这两个数执行1中POP出的操作符的对应运算,并将结果PUSH入数据栈中。
4.重复1-3,直到数据栈中只剩下一个数(或者操作符栈为空),那么数据栈中最后的元素便是运算结果!!!
以下为自己敲的代码:
#include<iostream>
#include<stdlib.h>
#include<string.h>
using
namespace
std;
#define
Msize
256
/**********************定义栈模板*********************/
template<typename
elemtype>//定义栈的类模板
class
Stack
typedef
struct
Node
elemtype
data;
struct
Node
*pro,
*next;
Node;
private:
Node
*base;
Node
*top;
public:
void
Init_Stack();
void
Push_Stack(elemtype
e);
void
Pop_Stack(elemtype
&e);
elemtype
Gettop();
bool
Istempt();
;
template<typename
elemtype>
void
Stack<elemtype>::Init_Stack()//初始化

base
=
(Node*)malloc(sizeof(Node));
if(NULL==base)
cout<<"申请存储空间失败!!";
exit(0);

top
=
NULL;
base->pro
=
NULL;
base->next
=
NULL;

template<typename
elemtype>
void
Stack<elemtype>::Push_Stack(elemtype
e)//入栈

Node
*s;
if(NULL==top)
top
=
base;
top->data
=
e;

else
s
=
(Node*)malloc(sizeof(Node));
if(NULL==s)
cout<<"申请空间失败!!";
exit(0);

s->next
=
NULL;
s->data
=
e;
s->pro
=
top;
top->next
=
s;
top
=
s;


template<typename
elemtype>
void
Stack<elemtype>::Pop_Stack(elemtype
&e)//出栈

Node
*p;
p
=
top;
if(NULL==top)
cout<<"栈已空!出栈失败!!"<<endl;
return;

e
=
top->data;
top
=
top->pro;
if(NULL!=top)
free(p);


template<typename
elemtype>
elemtype
Stack<elemtype>::Gettop()//取栈顶元素

if(NULL==top)
return
EOF;

else
return
top->data;


template<typename
elemtype>
bool
Stack<elemtype>::Istempt()//判断栈是否为空

if(NULL==top)
return
true;

else
return
false;


/****************************主函数****************************/
int
main()

Stack<float>
data;
Stack<int>
op;
char
str[Msize],
ctemp[10],
*q;
int
ntemp,
range,
flag
=
0,
i;
float
add1,
add2,
nresult;
op.Init_Stack();
data.Init_Stack();
cout<<"输入正确的表达式::"<<endl;
cin>>str;
for(i=0;i<strlen(str);i++)
//逆波兰运算
if(str[i]>='0'&&str[i]<='9')
q
=
ctemp;
while(str[i]>='0'&&str[i]<='9')
*q
=
str[i];
i++;
q++;

*q
=
'\0';
ntemp
=
atoi(ctemp);
data.Push_Stack(ntemp);
if(i>=strlen(str))
break;


if(str[i]=='(')
flag
=
flag
+
4;
continue;

if(str[i]==')')
flag
=
flag
-
4;
continue;

switch(str[i])
case
'+':
range
=
1
+
flag;
break;
case
'-':
range
=
2
+
flag;
break;
case
'*':
range
=
3
+
flag;
break;
case
'/':
range
=
4
+
flag;
break;
default
:
break;

if(op.Istempt())
op.Push_Stack(range);

else
while(range<op.Gettop())
data.Pop_Stack(add1);
data.Pop_Stack(add2);
op.Pop_Stack(ntemp);
switch(ntemp%4)
case
1:
data.Push_Stack(add2+add1);
break;
case
2:
data.Push_Stack(add2-add1);
break;
case
3:
data.Push_Stack(add2*add1);
break;
case
0:

if(0==add1)
cout<<"除数为0,错误!!!"<<endl;
exit(0);

data.Push_Stack(add2/add1);
break;

default
:
break;


op.Push_Stack(range);


while(!op.Istempt())
data.Pop_Stack(add1);
data.Pop_Stack(add2);
op.Pop_Stack(ntemp);
switch(ntemp%4)
case
1:
data.Push_Stack(add2+add1);
break;
case
2:
data.Push_Stack(add2-add1);
break;
case
3:
data.Push_Stack(add2*add1);
break;
case
0:

if(0==add1)
cout<<"除数为0,错误!!!"<<endl;
exit(0);

data.Push_Stack(add2/add1);
break;

default
:
break;


data.Pop_Stack(nresult);
cout<<endl<<"计算结果为::"<<endl;
cout<<str<<"
=
"<<nresult<<endl;
return
0;

以上代码不含表达式错误检测,计算时必须输入正确的算式!!!(纯手工制作,供楼主参考!望高手指正!!)
参考技术B 看严蔚敏的数据结构

在 C++ 中为链表类创建实例

【中文标题】在 C++ 中为链表类创建实例【英文标题】:Creating instances for a linked list class in C++ 【发布时间】:2018-10-21 07:31:18 【问题描述】:

我目前正在学习 C++ 链表,我从教科书中点击了这段代码。我无法理解:

const string& e

我正在尝试在我的主函数中为这个类编写一些实例,以查看事情是如何工作的,但不知道如何。 例如,我想在列表中添加 3、5、7 并在列表的前面添加 1,而不是从列表中删除 7。

#include <cstdlib>
#include <iostream>
#include <string>

using std::string;
using namespace std;



class StringNode                       // a node in a list of strings
private:
  string elem;                              // element value
  StringNode* next;                     // next item in the list

  friend class StringLinkedList;                // provide StringLinkedList 
                                                // access
;

class StringLinkedList                     // a linked list of strings
public:
  StringLinkedList();                       // empty list constructor
  ~StringLinkedList();                          // destructor
  bool empty() const;                       // is list empty?
  const string& front() const;                      // get front element
  void addFront(const string& e);               // add to front of list
  void removeFront();                       // remove front item list
private:
  StringNode* head;                     // pointer to the head of list
;

StringLinkedList::StringLinkedList()            // constructor
  : head(NULL)  

StringLinkedList::~StringLinkedList()           // destructor
   while (!empty()) removeFront(); 

bool StringLinkedList::empty() const            // is list empty?
   return head == NULL; 

const string& StringLinkedList::front() const       // get front element
   return head->elem; 



void StringLinkedList::addFront(const string& e)   // add to front of list
  StringNode* v = new StringNode;           // create new node
  v->elem = e;                          // store data
  v->next = head;                   // head now follows v
  head = v;                     // v is now the head




void StringLinkedList::removeFront()               // remove front item
  StringNode* old = head;               // save current head
  head = old->next;                 // skip over old head
  delete old;                       // delete the old head

【问题讨论】:

const string &amp;e 是对常量(只读)字符串的引用。它用于例如void addFront(const string&amp; e); 提供对现有字符串的引用(仅可用于读取访问),而无需复制字符串(内容)本身。 (这不会阻止 addFront()e 复制到 v-&gt;elem。) 【参考方案1】:

我试图寻找解释 C++ 如何使用按值调用的副本。

在 C++ 中,T 类型的函数参数将在调用函数之前制作对象的副本。

int myFunction( int value )

     value = value + 1;


int main( int argc, char * argv[] )

    int elem = 6;
    myFunction( elem );
    printf( "%d\n", elem ); // elem = 6;

在上面的例子中,int 值的副本被发送到myFunction,并且副本递增。

这可能不是我们想要的,我们可以通过修改myFunction 来获取值的引用,将结果更改为 7。这是通过使用“&”来描述参考值来完成的。

int myFunction( int & value )

     value = value + 1;


int main( int argc, char * argv[] )

    int elem = 6;
    myFunction( elem );
    printf( "%d\n", elem ); // elem = 7;

在上述情况下,没有复制,并且 elem 得到更新。

将引用传递给函数有两个主要原因

    允许更新值(或对象)。 为了避免复制值的成本。

在您引用的示例中,第二种情况是使用 const string &amp; 的原因(对字符串对象的引用)。 std::string,构造和销毁都有成本,所以通过发送引用来避免这种情况,效率更高。

为了补充这种用法,引用通常是const,以使编译器相信不应更改该值。

【讨论】:

以上是关于c++链表类模板问题(不要用c语言,用c++)的主要内容,如果未能解决你的问题,请参考以下文章

什么是C语言设计模板结构?

动态分配的双向链表类实例 segfault C++

C 语言链表问题

线性表-顺序表链表类模板的实现(数据结构基础 第2周)

双向链表的增删改查C++完整实现

在 C++ 中为链表类创建实例