线性表的顺序存储顺序表部分算法(注:以下算法注意鲁棒性和有效性的判断)画图很重要!!!
//将顺序表的所有元素逆置,空间复杂度为O(1)
{
i a[i]=a[n-i-1] //交换这两个存储空间中的元素值 } //删除顺序表中所有值为x的元素 { int k=0; // 记录不等于x的元素个数 for(i=0;i if(a[i]!=k){ a[k]=a[i]; k++; //不等于x的元素增1 } n=k; //循序表长度变为k } //删除有序顺序表L中值在给定值s与t之间的所有元素 { for(i=0;i //若i>n,说明所有的元素值均小于s,则return for(j=i;j for(;j a[i]=a[j]; //前移,填补被删元素位置 n=i; //顺序表长度变为i } //删除顺序表中值在给定值s与t之间(要求s /* 算法思想:从前向后扫描顺序表,用k记录下元素值在s到t之间元素的个数(初始时k=0) 对于当前扫描的元素,若值不在s到t之间,则前移k个位置;否则执行k++ 由于每个不在s到t之间的元素仅移动一次,所以算法效率高 */ { //判断若线性表为空或s和t不合法,返回 for(i=0;i { if(a[i]>=s&&a[i]<=t) k++; else a[i-k]=a[i];//当前元素前移k个位置 }//for n=n-k; //长度减少 } //从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不相同 {//i存储第一个不相同的元素,j工作指针 for(i=0;j=1;j { if(a[i]!=a[j]) //查找下一个与上一个元素值不同的元素 a[++i]=a[j]; //找到后将元素前移 } n=i+1; } //合并有序顺序表a和b成为一个新的有序顺序表c { //判断a+b的表长大于c的表长,则返回false while(i { if(a[i]<=b[j]) c[k++]=a[i++]; else c[k++]=b[j++]; } while(i c[k++] = a[i++]; while(j c[k++] = b[j++] cLen = k; } //一个一维数组中存了两个线性表,要求把这两个线性表位置交换 /* 算法思想:首先将a[m+n]中的全部元素原地逆置为(bn,bn-1,...,b1,am,am-1,...,a1) 在对前n个元素和后m个元素分别使用逆置算法。 */ void reverse(int a[],int left,int right,int arraySize) { //有效性判断 int mid = (left+right)/2; for(int i = 0;i < mid-left;i++) { temp = a[left+i]; a[left+i] = a[right-i]; a[right-i] = temp; } } void exchange(int a[], int m, int n, int arraySize) { reverse(a, 0 , m+n-1 , arraySize); reverse(a, 0 , n-1 , arraySize); reverse(a, n , m+n-1 , arraySize ); } //将数组a中保存的序列循环左移p个位置,要求时间复杂度O(n),空间复杂度为O(1) void reverse(int a[], int from, int to) { int i, temp; for(i=0;i<(to-from+1)/2;i++) { temp = a[from+i]; a[from+i] = R[to-i]; R[to-i] = temp; }//for }//reverse void converse(int a[], int n, int p) {//可以想成先将a逆置,再将b逆置,最后将a逆b逆一起逆置得到ba reverse(a, 0, p-1); reverse(a, p, n-1); reverse(a, 0, n-1); } //11年408中位数问题 中位数=L/2,两个序列的中位数是含他们所有元素的升序序列的中位数 /* 时间复杂度为O(log2n),空间复杂度为O(1) 本别求两个升序序列a、b的中位数,设为a和b (1)若a==b ,则a或b即为所求的中位数,算法结束 (2)若a
(3) 若a>b,则舍弃序列a中较大的一半,同时舍弃b中较小的一半,要求两次舍弃的长度相等 重复1、2、3直到两个序列中都只有一个元素时为止,较小者即为所求的中位数 */ int M_Search(int a[], int b[], int n) { int s1=0,d1=n-1,m1,s2=0,d2=n-1,m2; //分别表示a和b中首位数、某位数、中位数 while(s1!=d1 || s2!=d2) { m1 = (s1+d1)/2; m2 = (s2+d2)/2; if(a[m1]=a[m2]) return a[m1]; //满足条件一 if(a[m1]
{ if((s1+d1)%2==0) //如果元素个数为奇数 { s1 = m1; //舍弃a中间点以前的部分。且保留中间点 d2 = m2; //舍弃b中间点以后的部分且保留中间点 } else //元素个数为偶数 { s1 = m1+1 ; //舍弃a中间点及中间点以前的部分 d2 = m2; //舍弃b中间点以后部分且保留中间点 } } else //满足条件三 { if((s2+d2)%2==0) //若元素个数为奇数 { d1 = m1; //舍弃a中间点以后的部分且保留中间点 s2 = m2; //舍弃b中间点以前的部分且保留中间点 } else //元素个数为偶数 { d1 = m1; //舍弃a中间点以后部分且保留中间点 s2 = m2 + 1; //舍弃b中间点及中间点以前部分 } } } return a[s1] < b[s2] ? a[s1] : b[s2]; } //13年408主元素 /* 基本思想: 从前向后扫描数组元素,标记出一个可能成为主元素的元素Num。然后重新计数,确认Num是否是主元素 分为两步: 1、选取候选的主元素:依次扫描所给数组中的每个整数,将第一个遇到的整数Num保存到c中,记录Num的出现次数为1;若遇到下一个整数仍等于Num,则计数加1,否则计数减1;当计数减到0时,将遇到的下一个整数保存到c中,计数重新记为1,开始新一轮的计数,即从当前位置开始重复上述过程,直到扫描完全部数组元素 2、判断c中元素是否是真正的主元素:再次扫描该数组,统计c中元素出现的次数,若大于n/2,则为主元素;否则序列中不存在主元素 */ //时间复杂度为O(n),空间复杂度为O(1) int Majority(int a[], int n) { int i,c,count=1; //c用来保存候选主元素,count用来计数 c=a[0]; for(i=1;i if(a[i]==c) count++; else if(count>0) count--; else { c=a[i]; count=1; } if(count>0) for(i=count=0;i if(a[i]==c) count++; if(count>n/2) return c; //c即为主元素 else return -1; //不存在主元素 } 线性表的链式存储单链表部分算法 //递归实现在单链表中删除值为x的结点 //需要借助一个递归工作栈,深度为O(n),时间复杂度为O(n) void del(LinkList &l, int x) { NODE *p; //p指向待删除结点 if(l == NULL) //递归出口 return ; if(l->data == x) //若l所指的结点的值为x { p=l; //删除指针l,并让l指向下一结点 l=l->next; free(p); del(l,x); //递归调用 } else //若l所指结点的值不是x del(l->next,x); //递归调用 } //从头到尾输出单链表中每个结点的值(带头结点) void R_Print(NODE *p) { if(p->next!=NULL) R_Print(p->next); //递归 print(p->data); } 所谓的就地就是指辅助空间为O(1) //将单链表逆置,时间复杂度为O(n),空间复杂度为O(1),带头结点 NODE* reverse(NODE *head) { NODE *p, *s; //p为工作指针,s为p的后继,以防断链 p=head->next; //从第一个元素结点开始 head->next=NULL; //先将头结点的next域置空 while(p!=NULL) //依次将元素结点摘下 { s=p->next; //暂存p的后继 p->next=head->next; //将p结点插入到头结点之后 head->next=p; p=s; } return head; } //两个整数序列组成的单链表A和B,判断B是否是A的子序列 /* 算法思想: 若对应数据相等则后移指针;若对应数据不等,则A链表从上次开始比较结点的后继开始, B链表仍从第一个结点开始比较,直到B链表到尾表示匹配成功。 A链表到尾而B链表未到尾表示失败。 操作中应记住A链表每次的开始结点,以便下次匹配时好从其后继开始。 */ int pattern(NODE* A,NODE* B) {//判断B是否尾A的子链表,A、B均无头结点 NODE *p=A; //P为A链表的工作指针 NODE *pre=p; //pre记住每趟比较中A链表的开始结点 NODE *q=B; //q为B链表的工作指针 while(p&&q) if(p->data==q->data) { p=p->next; q=q->next; } else { pre=pre->next; p=pre; //A链表新的开始比较结点 q=B; //q从B链表第一个结点开始 } if(q==NULL) return 1; //B已经比较结束,B是A的子数列 else return 0; //B不是A的子数列 } //判断带头结点的循环双链表是否对称 /* 算法思想: 让p从左向右扫描,q从右向左扫描,直到它们指向同一结点(p==q,当循环双链表中结点个数为奇数时) 或相邻(p->next==q或q->prior==p,当循环双链表中结点个数为偶数时)为止,若它们所指的结点值相同, 则继续进行下去,否则返回0。若比较全部相等,则返回1. */ int symmetry(NODE* head) {//从两头扫描循环双链表,以判断链表是否对称 NODE *p=head->next,*q=head->prior; while(p!=q&&q->next!=p) { if(p->data==q->data) { p=p->next; q=q->next; } else return 0; } return 1; } //p38访问频度动双链表 /* 算法思想: 在双链表中查找数据值为x的结点,查到后,将结点从链表上摘下,然后再顺着结点的前驱链查找该结点的插入位置。 链表因频度递减,向前查找第一个比它频度大的结点,插入位置为该结点之后 */ NODE* Locate(NODE *head, int x) {//先查找数据x,查找成功时结点的访问频度域增1,然后将该结点按频度递减插入链表中适当位置(同频度最近访问的最前面) NODE *p=head->next,*q ;//p为工作指针,q为p的前驱,用于查找插入的位置 while(p&&p->data!=x) p=p->next; if(p==NULL) return NULL; //不存在查找值x else { p->freq++; //令元素值为x的结点的freq域加1 p->next->pred=p->pred; //pred表示前一个 p->pred->next=p->next;//将p结点从链表上摘下 q=p->pred; //下面查找p的插入位置 while(q!=head&&q->freq<=p->freq) q=q->fred; p->next=q->next; q->next->pred=p; //将p结点插入,一定是排在同频率的第一个 p->pred=q; q->next=p; } return p; } //非线性结构二叉树相关算法 //后续遍历二叉树非递归遍历思想 /* 因为后序非递归遍历二叉树的顺序是先访问左子树,再访问右子树,最后访问根结点。应当用堆栈来存储结点 */ //判断一颗二叉树是否为完全二叉树 /* 算法思想: 采用层次遍历的算法,将所有结点加入队列(包括空结点),当遇到空结点时,查看其后是否有非空结点, 若有,则二叉树不是完全二叉树 */ bool IsComplete(BiTree T) { InitQueue(Q); 若是空树,return 1; EnQueue(Q,T); while(!IsEmpty(Q)) { DeQueue(Q,p); if(p) //结点非空将其左右子树压入队列 { EnQueue(Q,p->lchild); EnQueue(Q,p->rchild); } else while(!IsEmpty(Q)){ DeQueue(Q,p); if(p) //结点非空,则二叉树为非完全二叉树 return 0; } } } //采用递归的算法判断两个二叉树是否相似 /* 算法思想: 若T1和T2都是空树,则相似 若一个为空,另一个不为空,则必然不相似 说这递归的比较它们的左右子树是否相似。 */ int similar(BiTree T1, BiTree T2) { int leftS, rightS; if(T1==NULL&&T2==NULL) //两树皆空,返回1 else if(T1==NULL || T2==NULL) //只有一树空 return 0; else { leftS=similar(T1->lchild,T2->lchild); rightS=similar(T1->rchild,T2->rchild); return leftS&&rightS; } } 图的相关概念 连通图:图G中任意两个顶点都是连通的。 连通分量(无向图的极大连通子图):极大即要求该连通子图包含其所有的边。 极小连通子图:既要保持图连通,又要使得边数最少的子图 强连通:在有向图中,从顶点V到顶点W,和从顶点W到顶点V都有路径 强连通图:图中任何一对顶点都是强连通的。 强连通分量:有向图中的极大强连通子图。 强连通图,强连通分量只是针对有向图而言。一般在无向图中讨论连通性,在有向图中讨论强连通性。 路径:即顶点Vi到顶点Vj的顶点序列 路径长度:路径上边的数目 回路/环:第一个顶点和最后一个顶点相同的路径 简单路径:在顶点序列中,顶点不重复出现的路径 简单回路:除第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路。