王道数据结构知识点整理

 

 

线性表的顺序存储顺序表部分算法(注:以下算法注意鲁棒性和有效性的判断)画图很重要!!!

//将顺序表的所有元素逆置,空间复杂度为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=s的第一个元素

//若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的顶点序列

路径长度:路径上边的数目

回路/环:第一个顶点和最后一个顶点相同的路径

 

简单路径:在顶点序列中,顶点不重复出现的路径

简单回路:除第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(本科学习杂烩,数据结构)