1、二叉树遍历 node
一、前序遍历(PreOrder)\中序遍历(InOrder)\后序遍历(PostOrder) ios
void PreOrderTraverse(BinaryTreeNode T) { if(T == NULL)return; cout<<T->node); PreOrderTraverse(T->lChild); PreOrderTraverse(T->lChild); }二、已知后序、中序,求前序遍历
#include<iostream.h> #include<string.h> #include<stdlib.h> #define MAX 20 /*预约义字符数组的最大长度*/ typedef struct BiTNode /*该结构体类型为树结点的类型*/ { char data; struct tnode *lchild; struct tnode *rchild; }BiTNode,*BiTree; void in_post_to_bt(char *in, char *post,int len,BiTree &T) { int k; if(len<=0){ T = NULL; return; } for(char *temp=in;temp<in+len;temp++)//在中序序列in中找到根节点所在的位置 if(*(post+len-1) == *temp){ k = temp-in;//k为根节点在中序序列中的下标 T = new BiTNode (); T->data = *temp; break; }} in_post_to_bt(in,post,k,T->lchild);//创建左子树 in_post_to_bt(in+k+1,post+k,len-k-1,T->rchild);//创建右子树 } void preOrder_visit(BiTree T) { if(T) { cout<<T->data; inOrder_visit(T->lchild); inOrder_visit(T->rchild); } } int main() { char in[MAX+1],post[MAX+1]; cout<<"输入中序序列:"; cin>>in; cout<<"输入后序序列:"; cin>>post; BiTNode T; int len_in=strlen(in),len_post=strlen(post); if(len_in<=MAX&&len_post<=MAX) in_post_to_bt(in,post,len_in,T); cout<<endl<<"输出前序序列:"; preOrder_visit(T); cout<<endl; }
二、排序算法 算法
七种排序算法,按复杂度分为两大类, 数组
//排序用到的顺序表 #define MAXSIZE 10 typedef struct { int r[MAXSIZE+1] ; int length; }SqList; //排序中大量用到数组交换 void swap(SqList *L,int i,int j) { int temp = L->r[i]; L->r[i] = L->r[j]; L->r[j] = temp; }(1)冒泡排序:两两比较相邻记录的关键字,若是反序则交换,直到没有反序的记录为止。
优化的冒泡排序算法 post
void BubbleSort(SqList *L) { int i,j; bool flag = true;//flag用来作标记 for(i=1,i<=L->length&&flag;i++){//若是flag为false则推出循环 flag = false; for(j=L->length-1;j>=i;j--){ if(L->r[j]>L->[j+1]) { swap(L,j,j+1); flag = true; } } } }
复杂度分析:增长了一个额外空间flag开销,若表自己有序,则根据改进后的代码,可推断就是n-1次比较,时间复杂度为O(n),最坏状况是,表逆序,比较1+2+3+。。。+(n-1)=(n-1)n/2,并做等数量及的移动,总时间复杂度为O(n^2),是一种稳定的排序算法。 性能
(2)简单选择排序:经过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n)个记录交换。 优化
void SelectSort( SqList *L) { int i,j,min; for(i=1;i<L->length;i++){ min = i; for(j=i;j<=L->length;j++){ if(L->r[min]>L->r[j]) L->r[min]=L->r[j]; } if(i!=min) swap(L,i,min); } }复杂度分析:增长一个min空间开销,不管最好最差的状况,其比较次数同样多,第i趟排序须要进行n-i此关键字比较,此时须要比较(n-1)+(n-2)+...+1=n(n-1)/2。而对于交换次数而言,当最好的时候,交换为0次,最差的时候,也就是初始降序,交换次序为n-1次,最终的排序时间是比较和交换的次数总和,所以,总的时间复杂度依然为O(n^2)。尽管与冒泡排序同为 O(n^2),但简单选择排序的性能仍是略优于冒泡排序的,是一种稳定的排序算法。
(3)直接插入排序:将一个记录插入到已经排好序的有序表中,从而获得一个新的、记录数增1的有序表。 spa
void InsertSort( SqList *L) { int i,j,temp; for(i=2;i<=L->length;i++){ if(L->r[i]<L-r[i-1]){ //需将L->r[i]插入有序表中 temp = L->r[i]; //记录当前比较元素 for(j=i-1;L->[j]>temp;j++) //有序部分记录后移 L->r[j+1]=L->r[j]; L->r[j+1]=temp; } } }复杂度分析:一个记录辅助空间开销,最好时即自己有序,比较次数即第六行每一个L.r[i]和L.r[i-1]的比较,因为每次都是 L.r[i]>L.r[i-1],因此不用比较,时间复杂度为O(n)。最坏时即自己逆序,须要比较2+3+4+...+n=(n+2)(n+3)/2次,而记录的移动次数也达到了最大值(n+4)(n-1)/2。若是记录是随机的,那根据几率相同的原则,平均比较次数和移动次数约为n^2/4次。所以,咱们得出直接插入排序的时间复杂度为O(n^2)。一样的时间复杂度,直接插入比冒泡排序、简单选择排序的性能要好一些, 是一种 稳定 的排序算法。
(4)希尔排序:跳跃分割,基本有序(小的关键字基本在前面,大的基本在后面)在直接插入排序基础上,将相距某个“增量”的记录组成一个子序列。 code
void InsertSort( SqList *L) { int i,j,temp, int increment = L->length; do{ increment = increment/3+1; //增量序列 for(i=increment+1;i<=L->length;i++){ if(L->r[i]<L-r[i-increment]){ //需将L->r[i]插入增量有序表中 temp = L->r[i]; //记录当前比较元素 for(j=i-increment;L->[j]>temp;j++)//有序部分记录后移 L->r[j+increment]=L->r[j]; L->r[j+1]=temp; } } }while(increment>1); }复杂度分析:增量的选取很关键,当增量序列为dlta[k]=2^(t-k+1)-1(0<=k<=t<=log2(n+1))增量序列的最后一个增量值必须等于1才行。希尔排序一种 不稳定的排序算法,能够得到好的效果,其时间复杂度为O(n^3/2)。
(5)堆排序:将待排序的序列构形成一个大顶堆。此时,整个序列的最大值就是对顶的根节点。将它移走(其实就是将其与堆数组末尾的元素交换,此时末尾元素就是最大值),而后将剩余的n-1个序列从新构形成一个堆,这样就会获得n个元素中的次大值。如此反复执行,能获得一个有序序列。。 排序
void InsertSort( SqList *L) { int i; for(i=L->length/2;i>0;i--) //把L中的r构建成一个大顶堆 HeapAdjust(L,i,L->length); for(i=L->length;i>1;i--){ //将堆顶记录和当前未经排序子序列的最后一个记录交换 swap(L,i,j); HeapAdjust(L,1,i-1); //将L->r[1,...i-1]从新调整为大顶堆 } } void HeapAdjust(SqList *L,int start,int end) { int temp,j; temp = L->r[start]; for(j=2*start;j<=end;j*=2){//沿关键字较大的孩子结点向下筛选 if(j<end && L->r[j]<r->r[j+1]) j++; if(temp>=L-r[j]) break; L->r[start] = L->r[j]; start = j; } L->r[start]=temp; }
复杂度分析:运行时间主要消耗在初始建堆和在重复建堆时的反复筛选上。由于咱们是彻底二叉树从最下层最右边的非终端结点开始构建,将它与其孩子进行比较如有必要的互换,对于非终端结点来讲,最多进行两次比较和互换操做,所以构建堆的时间复杂度为O(n)。正式排序时,第i次取堆顶记录重建堆须要用O(logi)的时间,而且须要取n-1次堆顶记录,所以,重建堆的时间复杂度为O(nlogn)。堆排序对原始状态不敏感,不管最好、最坏和平均时间复杂度均为O(nlogn)。空间复杂度上,只有用一个交换的暂存单元。不过记录的比较与交换是跳跃式的,因此堆排序也是不稳定的。
(6)归并排序:假设初始序列含有n个记录,则能够当作是n个有序的子序列,每一个子序列的长度为1,而后两两归并,获得[n/2](上表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,......,如此重复,直至获得一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
//对顺序表L做归并排序 void MergeSort(SqList *L) { MSort(L->r,L->r,1,L->length); } //将SR[start...end]归并为有序的TR1[start...end] void MSort(int SR[],int TR1[],int start,int end) { int m; int TR2[MAXSIZE+1]; if(start == end) TR1[start] = SR[start]; else { mid = (start+end)/2; //将SR[start...end]平分为SR[start...mid]和SR[mid...end] MSort(SR,TR2,start,mid); //递归将SR[start...mid]归并为有序的TR2[start...mid] MSort(SR,TR2,mid+1,end); //递归将SR[mid+1...end]归并为有序的TR2[mid+1...end] Merge(TR2,TR1,start,mid,end);//递归将TR2[start...mid]和TR2[mid+1...end]归并为有序的TR1[start...end] } } //将有序的SR[i..m]和SR[m+1...n]归并为有序的TR[i..n] void Merge (int SR[],int TR[],int i, int m,int n) { int j,k,l; for(j=m+1,k=i;i<=m&&j<=n;k++){ if(SR[i]<SR[j]) TR[k]=SR[i++]; else TR[k]=SR[j++]; } if(i<=m){ for(l=0;l=m-i;l++) TR[k+l]=SR[i+l]; } if(j<=n){ for(l=0;l<=n-j;l++) TR[k+l]=SR[j+l]; } }
算法复杂度:一趟归并须要将SR[1]~SR[n]中相邻的长度为h的有序序列进行两两归并,并将结果存放在到TR1[1]~TR1[n]中,须要将待排序序列中的全部记录扫描一遍,所以耗费O(n)时间,而有彻底二叉树的深度可知,整个归并排序须要进行[log2n](上取正)次,所以,总的时间复杂度为O(nlogn),并且是最好、最坏、平均的时间性能。
因为归并排序在归并过程当中须要与原始记录序列一样数量的存储空间存放归并结果以及递归深度为log2n的栈空间,所以空间复杂度为O(n+logn)。if(SR[i]<SR[j])语句,说明不存在跳跃,则归并排序是一种稳定的排序算法。
//归并的非递归算法 void MergeSort2(SqList *L) { int *TR = (int*) new (L->length*sizeof(int)); int k = 1; while(k<L->length){ MergePass(L->r,TR,k,L->length); k = 2*k; MergePass(TR,L->r,k,L->length); k = 2*k; } } void MergePass (int SR[],int TR[],int start,int end) { int i = 1; int j; while(i<=n-2*start+1){ Merge(SR,TR,i,i+start-1,i+2*start-1); //两两归并 i = i+2*start; } if(i<end-start+1) //归并最后两个序列 Merge(SR,TR,i,i+start-1,n); else for(j=i;j<=n;j++) TR[j] = SR[j]; }非递归的迭代方法,避免了递归时深度为log2n的栈空间,空间只用到申请并临时用的TR数组。空间复杂度为O(n)。
(7)快速排序:经过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另外一部分记录的关键字小,则可分别对这两部分记录继续进行排序,已达到整个序列有序的目的。
复杂度分析:在最优状况下,每次划分都很均匀,若是排序n个关键字,其地归树的深度为[log2n]+1(下取正)。第一次Partition应该是须要对整个数组扫描一遍,作n次比较。而后,得到的枢纽将数组一分为二,那么各自还须要T(n/2)的时间(注意是最好状况下),则最优状况下的,快速排序算法的时间复杂度为O(nlogn)。是一种 不稳定排序算法。#include <iostream>using namespace std;void Qsort(int a[], int low, int high){if(low >= high){return;}int first = low;int last = high;int key = a[first];/*用字表的第一个记录做为枢轴*/while(first < last){while(first < last && a[last] >= key){--last;}a[first] = a[last];/*将比第一个小的移到低端*/while(first < last && a[first] <= key){++first;}a[last] = a[first];/*将比第一个大的移到高端*/}a[first] = key;/*枢轴记录到位*/Qsort(a, low, first-1);Qsort(a, first+1, high);}int main(){int a[] = {57, 68, 59, 52, 72, 28, 96, 33, 24};Qsort(a, 0, sizeof(a) / sizeof(a[0]) - 1);/*这里原文第三个参数要减1不然内存越界*/for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++){cout << a[i] << "";}
优化方案:一、优化选取枢纽:三数取中;二、优化没必要要的交换