【数据结构】栈的应用 I :表达式求值

1. 介绍


表达式分为前缀、中缀、后缀。前缀表达式,也称为波兰表达式,其特点是将操作符置于操作数的前面。后缀表达式,也成为逆波兰表达式,所有操作符置于操作数的后面。波兰表达式、逆波兰表达式均是由波兰数学家Jan Łukasiewicz所提出的。中缀表达式将操作符放在操作数中间。前缀表达式和后缀表达式相对于中缀表达式最大的不同是,去掉了表示运算优先级的括号。


1.1 前缀表达式求值


求值过程中会用到栈,用以存储操作数和中间运算结果。


从右至左扫描前缀表达式,进行如下操作:

(1)若遇到操作数,则操作数入栈;

(2)若遇到二元操作符,出栈两个元素;若是一元操作符,出栈一个元素;进行操作符对应的运算,将运算结果入栈;

直至扫描完整个表达式,最后栈中只有一个元素,即为表达式的值。

1.2 后缀表达式求值


与前缀表达式求值过程基本相同,不同的是从左至右扫描表达式。


1.3 中缀表达式求值


求值方法与下面的中缀转后缀的算法有点类似。中缀表达式有括号,并且操作符有运算优先级,求值过程中会用到两个栈:opnd栈和oprt栈。opnd栈用以存储操作数或中间运算结果,oprt栈用以存储操作符或'('


从左至右扫描中缀表达式,每扫描到一个字符,做如下处理:

(1)若是操作数,则入opnd栈。

(2)若是操作符,则将其与oprt栈顶元素相比较,

①该操作符高于栈顶元素,说明该操作符的操作对象还没被扫描到,直接将该操作符压入oprt栈。

②该操作符的优先级低于oprt栈顶元素,说明该操作符前面的式子应该先运算。将oprt栈进行出栈操作直至该操作符的优先级高于栈顶元素(或栈顶元素为'(',或栈为空)。同时,opnd也对应地进行出栈操作,如果oprt栈顶元素是二元操作符,opnd出栈两个元素;如果oprt栈顶元素是一元操作符,opnd出栈一个元素;然后,进行相应的运算,将运算结果压入opnd栈。

(3)若遇到'(',直接将其压入oprt栈。

(4)若遇到')',说明需要对该括号内的式子进行运算;oprt栈进行出栈操作直至栈顶元素是'(',并且在出栈的过程中做对应于oprt栈顶元素的运算,如(2)中②所描述。将运算结果压入opnd栈。

扫描完中缀表达式后,oprt进行出栈操作直至栈为空,并做对应于oprt栈顶元素的运算,将运算结果压入opnd栈。最后,opnd栈的栈顶元素即为中缀表达式的值。


2. 中缀与后缀之间的转换


2.1 中缀转后缀


将中缀表达式转换为后缀表达式的经典算法是Shunting yard算法(也叫调度场算法)[3],由Dijkstra引入,因其操作类似火车编组场而得名。为了做简单的四则运算,这里将Shunting yard算法做了部分简化。


用栈存储操作符和左括号'('。


从左至右扫描中缀表达式,按如下情况进行处理:

(1)若遇到操作数,将操作数直接输出;

(2)若遇到操作符,有以下两类情况:

①操作符的运算优先级高于栈顶元素,表明该操作符的下一个操作对象还没被扫描到(针对的是二元操作符);将该操作符压入栈。

②操作符的运算优先级低于或等于栈顶元素,表明该操作符的两个操作对象已经输出了;进行出栈操作直至该操作符优先级高于栈顶元素(或栈顶元素为'(',或栈为空);然后,将该操作符压入栈。

(3)若遇到'(',直接将其压入栈;

(4)若遇到')',表明该括号内的式子扫描完毕;进行出栈操作直至栈顶元素是'(',并且出栈的过程中输出栈顶元素(相当于打印括号内的操作符)。

当扫描到中缀表达式的结尾时,将栈中元素出栈并输出。所得到的输出结果为所转化的后缀表达式。


2. 2 后缀转中缀


以操作符为非叶节点、操作数为叶节点的二叉树,可以用来表示表达式,并且其前序、中序、后序遍历分别为该表达式的前缀、中缀、后缀表示法。表达式,无论是前缀、中缀或是后缀,可以确定唯一一棵与之对应的二叉树。这里,操作符均指二元:'+','-','*','/'。由此易知,每一个操作符有左子树和右子树。我们称左子树所对应的表达式(字符串的形式)为该操作符的左子串,与此相应地有右子串


在中序遍历该二叉树时,遇到操作符时,对其子串是否加括号是一个非常棘手的问题,大概分四种情况考虑:

①若是'+',其左右子串均不需加括号。

②若是'-',仅需考虑其右子串是否加括号。如果右子串的根结点是'+'或'-'(也就是说右子串根结点的运算优先级与'-'的相等),则需加括号;若是'*'或'/',则不需要加括号。

③若是'*',需考虑左右子串是否加括号。如果右子串的根结点是'+'或'-'(也就是说左子串根结点的运算优先级小于'*'),则需加括号;若是'*'或'/',则不需要加括号。同样地,对左子串的进行一样的处理。

④若是'/',需考虑左右子串是否加括号。如果右子串的根结点是操作符,说明右子串是含有操作符的表达式,则需加括号。对左子串的处理方式与③相同,如果左子串的根结点是'+'或'-'(也就是说左子串根结点的运算优先级小于'/'),则需加括号;若是'*'或'/',则不需要加括号。

所谓对子串加括号,是指在子串的末和尾分别加上'('和')'。


将后缀转中缀有两种方法:

方法一:由后缀表达式建立相应的二叉树,再对二叉树进行中序遍历,所得结果即为中缀表达式。


方法二:用栈来实现。有两个栈,一个是opnd栈,用以存储子串或操作数;另一个是oprt栈,用以存储是操作符的子串根节点,如果子串的根结点是操作数,不予存储。注意到,在扫描后缀表达式时,子串的根结点都是最后才被扫描到。所以,也可以说oprt栈存储的是子串的最后一个操作符。


从左至右扫描后缀表达式,

(1)若遇到操作数,直接入opnd栈。

(2)若遇操作符,从opnd栈取出两个子串,栈顶是该操作符的右子串,栈顶的下一个是该操作符的左子串;对oprt栈,如果左右子串含操作符(也就是子串长度大于1),栈顶是左子串的最后一个操作符,栈顶的下一个是右子串的操作符。如果子串的长度大于1,说明该子串含有操作符,oprt栈做出栈操作。

(3)按照上面提及的规则,做加括号的处理。

(4)处理之后,操作符入oprt栈;左子串+操作符+右子串,构成新的子串,opnd栈。


3. Referrence


[1] 维基百科,波兰表示法

[2] 维基百科,逆波兰表示法

[3] 维基百科,Shunting yard算法

[4]ssjhust123,中缀表达式转换为后缀表达式


4. 问题


4.1 POJ 3295


题目大意:对波兰记法的前缀表达式,判断其是否为永真式。


有5个操作数p,q,r,s,and t,对应的取值情况有32种。为了取遍32种值,采用了网上代码的一个小技巧(i>>j)%2。


源代码:

3295 Accepted 184K 0MS C++ 1377B 2013-09-24 11:02:18
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;

stack<int>st;
int val[5];
bool flag;
char str[101];

void calculate(int len)
{
	int i;
	int temp1,temp2;
	for(i=len-1;i>=0;i--)
	{
		switch(str[i])
		{
		case 'K':
			{
				temp1=st.top();
				st.pop();
				temp2=st.top();
				st.pop();
				st.push(temp1&&temp2);
				break;
			}
		case 'A':
			{
				temp1=st.top();
				st.pop();
				temp2=st.top();
				st.pop();
				st.push(temp1||temp2);
				break;
			}
		case 'N':
			{
				temp1=st.top();
				st.pop();
				st.push(!temp1);
				break;
			}
		case 'C':
			{
				temp1=st.top();
				st.pop();
				temp2=st.top();
				st.pop();
				st.push(!temp1||temp2);
				break;
			}
		case 'E':
			{
				temp1=st.top();
				st.pop();
				temp2=st.top();
				st.pop();
				st.push(temp1==temp2);
				break;
			}
		default:
			st.push(val[str[i]-'p']);
		}
	}
}

int main()
{
	int i,j,len;
	while (scanf("%s",&str)&&str[0]!='0')
	{
			len=strlen(str);
			flag=true;
			while(!st.empty())          //clear stack
				st.pop();

			for(i=0;i<32;i++)
			{
				for(j=0;j<5;j++)
					val[j]=(i>>j)%2;

				calculate(len);
				if(!st.top())
				{
					flag=false;
					break;
				}
			}

			if(flag)
				printf("tautology\n");
			else
				printf("not\n");
	}
	return 0;
}


4.2 POJ 2106


题目大意:对中缀记法的的布尔表达式进行求值。


非运算'!'是一元运算,并且具有最高运算优先级。输入有诸如:"!!!F"的,需要考虑清楚。


没考虑到"!!!F",RE了一次;还有一些情况没考虑到,WA了几次。逻辑关系一定要理清楚。


源代码:

2106 Accepted 184K 0MS C++ 1801B 2013-09-30 17:56:35
#include <iostream>
#include <stack>
#include <map>
using namespace std;

stack<char>oprt;
stack<int>opnd;
map<char,int>priority;

int compute(int a,int b,char op)
{
	if(op=='&')
		return a&&b;
	else if(op=='|')
		return a||b;
}

int evaluate(char *in)
{
	int i,temp1,temp2;
	while(!oprt.empty())
		oprt.pop();
	while(!opnd.empty())
		opnd.pop();
	
	int len=strlen(in);
	for(i=0;i<len;i++)
	{
		if(in[i]==' ')
			continue;
		else if(isupper(in[i]))
			opnd.push(in[i]=='V');
		else
		{
			switch(in[i])
			{
			case '&': case '|':
				while(!oprt.empty()&&oprt.top()!='('&&priority[in[i]]<=priority[oprt.top()])
				{
					temp1=opnd.top();
					opnd.pop();
					if(oprt.top()=='!')
						opnd.push((1+temp1)%2);
					else
					{
						temp2=opnd.top();
						opnd.pop();
						opnd.push(compute(temp2,oprt.top()));
					}
					oprt.pop();
				}
				oprt.push(in[i]);
				break;
			case '!': case '(':
				oprt.push(in[i]);
				break;
			case ')':
				while(oprt.top()!='(')
				{
					temp1=opnd.top();
					opnd.pop();
					if(oprt.top()=='!')
						opnd.push((1+temp1)%2);
					else
					{
						temp2=opnd.top();
						opnd.pop();
						opnd.push(compute(temp2,oprt.top()));
					}
					oprt.pop();
				}
				oprt.pop();
				break;
			}
		}
	}
	
	while(!oprt.empty())
	{
		temp1=opnd.top();
		opnd.pop();
		if(oprt.top()=='!')
			opnd.push((1+temp1)%2);
		else
		{
			temp2=opnd.top();
			opnd.pop();
			opnd.push(compute(temp2,oprt.top()));
		}
		oprt.pop();
	}
	return opnd.top();
}

int main()
{
	int count=1;
	char in[150];
	priority['|']=1; priority['&']=2; priority['!']=3;
	
	while(gets(in))
		printf("Expression %d: %c\n",count++,evaluate(in)?'V':'F');
	return 0;
}


4.3 POJ 1686


题目大意:判断两个表达式是否相等。


思路:将变量的值带入计算:0~9按0~9处理,其他变量取ASCII码值。若两个表达式的值相等,即说明两个表达式相等;反之,则否。中缀表达式求值太麻烦了,先将其转化为后缀表达式,再求值。


不过,这种方法会误判式子a+d与b+c相等。不过,好在测试数据没这么BT。


写代码时,犯了好多低级错误。还有,scanf遇到空格、回车等会停止读取,结束这一次的输入。若输入的字符串带空格、tab的话,用gets。


源代码:

1686 Accepted 188K 0MS C++ 1815B 2013-09-25 17:32:35
#include <iostream>
#include <map>
#include <stack>
using namespace std;

map<char,int>priority;

/*transform the infix to the postfix*/
void transform(char *str)
{
	stack<char>st1;
	int i,len;
	char temp[80];
	len=strlen(str);
	while (!st1.empty())
		st1.pop();
	
	for(i=0,j=0;i<len;i++)
	{
		if(isalnum(str[i]))
			temp[j++]=str[i];
		else
		{
			switch(str[i])	
			{
			case '+': case '-': case'*':
				while (!st1.empty()&&st1.top()!='('&&priority[str[i]]<=priority[st1.top()])
				{
					temp[j++]=st1.top();
					st1.pop();
				}
				st1.push(str[i]);
				break;
			case '(':
				st1.push(str[i]);
				break;
			case ')':
				while (st1.top()!='(')
				{
					temp[j++]=st1.top();
					st1.pop();
				}
				st1.pop();
				break;
			}
		}
	}
	while (!st1.empty())
	{
		temp[j++]=st1.top();
		st1.pop();
	}
	temp[j]='\0';
	strcpy(str,temp);
}

/*calculate the result of a postfix expression*/
int calculate(char *s,int len)
{
	int i;
	int temp1,temp2;
	stack<int>st2;
	for(i=0;i<len;i++)
	{
		
		if(isdigit(s[i]))
			st2.push(s[i]-'0');
		else if(isalpha(s[i]))
			st2.push(s[i]);
		else
		{
			temp1=st2.top();
			st2.pop();
			temp2=st2.top();
			st2.pop();	
			switch(s[i])
			{
			case '+':
				st2.push(temp1+temp2);
				break;
			case '-':
				st2.push(temp2-temp1);
				break;
			case '*':
				st2.push(temp1*temp2);
				break;
			}
		}
	}
	return st2.top();
}

int main()
{
	int N,result1,result2;
	char str1[80],str2[80];
	priority['+']=1;
	priority['-']=1;
	priority['*']=2;
    scanf("%d",&N);
	getchar();
	while (N--)
	{
		gets(str1);
		transform(str1);;
        result1=calculate(str1,strlen(str1));
		
		gets(str2);
		transform(str2);
        result2=calculate(str2,strlen(str2));
		
		if(result1==result2)
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}

4. 4 POJ 1400


题目大意:对中缀表达式去除多余的括号。


现将中缀转后缀,再将后缀转中缀。


调试、思考了一个晚上,一早起来灵感迸发,写出来了。


源代码:

1400 Accepted 256K 704MS C++ 2586B 2013-09-30 09:10:0
#include<iostream>
#include<string>
#include <stack>
#include <map>
using namespace std;

stack<char>oprt;
stack<string>opnd;
map<char,int>priority;

string infix2postfix(string in)
{
	int i;
	string post="";
	while(!oprt.empty())
		oprt.pop();
	
	for(i=0;i<in.length();i++)
	{
		if(isalpha(in[i]))
			post+=in[i];
		else
		{
			switch(in[i])
			{
			case '+': case '-': case '*': case '/':
				while(!oprt.empty()&&oprt.top()!='('&&priority[in[i]]<=priority[oprt.top()])
				{
					post+=oprt.top();
					oprt.pop();
				}
				oprt.push(in[i]);
				break;
			case '(':
				oprt.push(in[i]);
				break;
			case ')':
				while(oprt.top()!='(')
				{
					post+=oprt.top();
					oprt.pop();
				}
				oprt.pop();
				break;
			}
		}
	}
	
	while(!oprt.empty())
	{
		post+=oprt.top();
		oprt.pop();
	}
	return post;
}

string postfix2infix(string post)
{
	int i;
	string temp1,temp2;
	char top_oprt,next_top;
	while(!oprt.empty())
		oprt.pop();
	while(!opnd.empty())
		opnd.pop();
	
	for(i=0;i<post.length();i++)
	{
		if(isalpha(post[i]))
		{
			temp1="";
			temp1+=post[i];
			opnd.push(temp1);
		}
		else
		{
			temp1=opnd.top();
			opnd.pop();
			temp2=opnd.top();
			opnd.pop();
			if(temp1.length()>1)
			{
				top_oprt=oprt.top();
				oprt.pop();
			}
			if(temp2.length()>1)
			{
				next_top=oprt.top();
				oprt.pop();
			}
			switch(post[i])
			{
			case '-':
				if(temp1.length()>1&&priority[top_oprt]==priority['-'])  //add parenthese
				{
					temp1.insert(0,"(");
					temp1.insert(temp1.length(),")");
				}
				break;
			case '*':
				if(temp1.length()>1&&priority[top_oprt]<priority['*'])
				{
					temp1.insert(0,")");
				}
				if(temp2.length()>1&&priority[next_top]<priority['*'])
				{					
					temp2.insert(0,"(");
					temp2.insert(temp2.length(),")");
				}
				break;
			case '/':
				if(temp1.length()>1)
				{
					temp1.insert(0,")");
				}
				if(temp2.length()>1&&priority[next_top]<priority['/'])
				{
					temp2.insert(0,")");
				}
				break;
			}
			oprt.push(post[i]);
			temp2+=post[i];
			temp2+=temp1;
			opnd.push(temp2);
		}
	}
	return opnd.top(); 
}

int main()
{
	int N;
	string in,post;
	priority['+']=priority['-']=1;
	priority['*']=priority['/']=2;
	
	cin>>N;
	while(N--)
	{
		cin>>in;
		post=infix2postfix(in);
		in=postfix2infix(post);
		cout<<in<<endl;
	}
	return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


【啊哈!算法】算法3:最常用的排序——快速排序       上一节的冒泡排序可以说是我们学习第一个真正的排序算法,并且解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了O(N2)。假如我们的计算机每秒钟可以运行10亿次,那么对1亿个数进行排序,桶排序则只需要0.1秒,而冒泡排序则需要1千万秒,达到115天之久,是不是很吓人。那有没有既不浪费空间又可以快一点的排序算法
匿名组 这里可能用到几个不同的分组构造。通过括号内围绕的正则表达式就可以组成第一个构造。正如稍后要介绍的一样,既然也可以命名组,大家就有考虑把这个构造作为匿名组。作为一个实例,请看看下列字符串: “08/14/57 46 02/25/59 45 06/05/85 18 03/12/88 16 09/09/90 13“ 这个字符串就是由生日和年龄组成的。如果需要匹配年两而不要生日,就可以把正则
选择排序:从数组的起始位置处开始,把第一个元素与数组中其他元素进行比较。然后,将最小的元素方式在第0个位置上,接着再从第1个位置开始再次进行排序操作。这种操作一直到除最后一个元素外的每一个元素都作为新循环的起始点操作过后才终止。 public void SelectionSort() { int min, temp;
public struct Pqitem { public int priority; public string name; } class CQueue { private ArrayList pqueue; public CQueue() { pqueue
在编写正则表达式的时候,经常会向要向正则表达式添加数量型数据,诸如”精确匹配两次”或者”匹配一次或多次”。利用数量词就可以把这些数据添加到正则表达式里面了。 数量词(+):这个数量词说明正则表达式应该匹配一个或多个紧紧接其前的字符。 string[] words = new string[] { "bad", "boy", "baad", "baaad" ,"bear", "b
来自:http://blog.csdn.net/morewindows/article/details/6678165/归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列
插入排序算法有两层循环。外层循环会啄个遍历数组元素,而内存循环则会把外层循环所选择的元素与该元素在数组内的下一个元素进行比较。如果外层循环选择的元素小于内存循环选择的元素,那么瘦元素都想右移动以便为内存循环元素留出位置。 public void InsertionSort() { int inner, temp;
public int binSearch(int value) { int upperBround, lowerBound, mid; upperBround = arr.Length - 1; lowerBound = 0; while (lowerBound <= upper
虽然从表内第一个节点到最后一个节点的遍历操作是非常简单的,但是反向遍历链表却不是一件容易的事情。如果为Node类添加一个字段来存储指向前一个节点的连接,那么久会使得这个反向操作过程变得容易许多。当向链表插入节点的时候,为了吧数据复制给新的字段会需要执行更多的操作,但是当腰吧节点从表移除的时候就能看到他的改进效果了。 首先需要修改Node类来为累增加一个额外的链接。为了区别两个连接,这个把指
八、树(Tree)树,顾名思义,长得像一棵树,不过通常我们画成一棵倒过来的树,根在上,叶在下。不说那么多了,图一看就懂:当然了,引入了树之后,就不得不引入树的一些概念,这些概念我照样尽量用图,谁会记那么多文字?树这种结构还可以表示成下面这种方式,可见树用来描述包含关系是很不错的,但这种包含关系不得出现交叉重叠区域,否则就不能用树描述了,看图:面试的时候我们经常被考到的是一种叫“二叉树”的结构,二叉
Queue的实现: 就像Stack类的实现所做的一样,Queue类的实现用ArrayList简直是毋庸置疑的。对于这些数据结构类型而言,由于他们都是动态内置的结构,所以ArrayList是极好的实现选择。当需要往队列中插入数据项时,ArrayList会在表中把每一个保留的数据项向前移动一个元素。 class CQueue { private ArrayLis
来自:http://yingyingol.iteye.com/blog/13348911 快速排序介绍:快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地
Stack的实现必须采用一种基本结构来保存数据。因为再新数据项进栈的时候不需要担心调整表的大小,所以选择用arrayList.using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Collecti
数组类测试环境与排序算法using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Data_structure_and_algorithm{ class CArray { pr
一、构造二叉树 二叉树查找树由节点组成,所以需要有个Node类,这个类类似于链表实现中用到的Node类。首先一起来看看Node类的代码。 public class Node { public int Data; public Node Left; public Node Right; public v
二叉树是一种特殊的树。二叉树的特点是每个结点最多有两个儿子,左边的叫做左儿子,右边的叫做右儿子,或者说每个结点最多有两棵子树。更加严格的递归定义是:二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树。 下面这棵树就是一棵二叉树。         二叉树的使用范围最广,一棵多叉树也可以转化为二叉树,因此我们将着重讲解二叉树。二叉树中还有连两种特殊的二叉树叫做满二叉树和
上一节中我们学习了队列,它是一种先进先出的数据结构。还有一种是后进先出的数据结构它叫做栈。栈限定只能在一端进行插入和删除操作。比如说有一个小桶,小桶的直径只能放一个小球,我们现在向小桶内依次放入2号、1号、3号小球。假如你现在需要拿出2号小球,那就必须先将3号小球拿出,再拿出1号小球,最后才能将2号小球拿出来。在刚才取小球的过程中,我们最先放进去的小球最后才能拿出来,而最后放进去的小球却可以最先拿
msdn中的描述如下:(?= 子表达式)(零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,w+(?=d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。(?(零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?此构造不会回溯。msdn描述的比较清楚,如:w+(?=ing) 可以匹配以ing结尾的单词(匹配结果不包括ing),(
1.引入线索二叉树 二叉树的遍历实质上是对一个非线性结构实现线性化的过程,使每一个节点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。但在二叉链表存储结构中,只能找到一个节点的左、右孩子信息,而不能直接得到节点在任一遍历序列中的前驱和后继信息。这些信息只有在遍历的动态过程中才能得到,因此,引入线索二叉树来保存这些从动态过程中得到的信息。 2.建立线索二叉树 为了保
排序与我们日常生活中息息相关,比如,我们要从电话簿中找到某个联系人首先会按照姓氏排序、买火车票会按照出发时间或者时长排序、买东西会按照销量或者好评度排序、查找文件会按照修改时间排序等等。在计算机程序设计中,排序和查找也是最基本的算法,很多其他的算法都是以排序算法为基础,在一般的数据处理或分析中,通常第一步就是进行排序,比如说二分查找,首先要对数据进行排序。在Donald Knuth 的计算机程序设