C语言 一、二进制和位运算——异或的高端操作 ### 问题导入:
袋子里一共有a个白球,b个黑球,每次从袋子里拿2个球,每个球每次被拿出的机会均等,如果拿出的是2个白球、或者2个黑球,那么就往袋子中重新放入一个白球,如果拿出的是1个白球和1个黑球,那么就往袋子中重新放入1个黑球。那么最终袋子中一定只会有1个球,请问最终的球是黑球的概率是多少?用a和b来表达这个概率。
答案: ==若原始黑球数量为偶数,则最终的球是黑球的概率为0%,若原始黑球数量为奇数,则最终的球是黑球的概率为100%。完全和白球的数量无关,通过异或运算的 性质解决。==
1.异或运算性质 :1)异或运算就是无进位相加,即1与0异或为1,0与0、1与1异或为0例:(8位)A:01101110
B:10011101
则C=A ^ B:11110011
2)异或运算满足交换律、结合律,即同一批数字,无论异或顺序如何,结果都一致。
3)0 ^ n=n
,n ^ n=0
4)整体异或和(所有数字异或的结果)如果为x,若整体其中某个部分的异或和为y,则剩下部分的异或和为x ^ y,例:c=a ^ b,a=c ^ b,b=a ^ c
==性质4在算法中的用处较多==
2.题解: 将白球看作0,黑球看作1。即0与1相遇产生1,0与0、1与1相遇均产生0,即为异或运算。则问题根据异或运算的性质,问题可以转化为,a个0同b个1作异或运算的结果。因此当b为偶数时,最终异或结果为0,为白球,概率为0%;当b为奇数时,最终异或结果为1,为黑球,概率为100%。
3.异或运算的骚操作 :1).交换两个数: 1 2 3 4 5 6 int a=10 ;int b=20 ; a=a^b; b=a^b; a=a^b;
2).不用任何判断语句和比较操作,返回两个数的最大值: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 int flip (int n) { return n^1 ; }int sign (int n) { flip((unsigned int )n>>31 ); }int getMax (int a,int b) { int c=a-b; int signa=sign(a); int signb=sign(b); int signc=sign(c); int is_diffab=signa^signb; int is_sameab=flip(is_diffab); int returna=is_diffab*signa+is_sameab*signc; int returnb=flip(returna); return a*returna+b*returnb; }
3). 找到缺失的数字: 0到10,11个数中缺一个数,求缺失的数字是多少?
运用异或的运算性质,==总异或和去异或数组中有的数字的异或和==得到的结果即为所缺的那一个数字。
4).找出数组中唯一出现奇数次的数字: 遍历整个数组的同时用一个变量ans(初始化为0)存储数组中按序异或即最终异或和,该数即为所求。运用到了==异或的偶消奇留==的性质。
5).骚操作 ①Brian Kernighan算法——提取出二进制状态中最右侧的1 n&(~n+1)
或(n&(-n))
②数组中有2种数出现了奇数次,其他的数都只出现了偶数次,返回这2种出现了奇数次的数:a、b(a!=b,显然) 先用eor1将数组全部数字异或和存储得到的数即为a ^ b,根据a ^ b最右侧的1(假定为第i位)(采用Brian Kernighan算法)将数组中的数分为第i位为1和第i位为0的两个阵营,再遍历一遍数组,用eor2将第i位为1的数的异或和储存下来,则eor2为a和b中的一个数,用eor1去异或eor2则可得到另一个数。
code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void findoddnum (int *nums,int len) { int eor1=0 ; int i=0 ; for (i;i<len;i++){ eor1^=nums[i]; } int rightOne=eor1&(-eor1); int eor2=0 ; for (i=0 ;i<len;i++){ if (nums[i]&rightOne==0 ){ eor2^=nums[i]; } } printf ("%d %d" ,eor2,eor1^eor2); }
6).找出数组中唯一的出现次数小于m的数: 将数组中的数以二进制表示后将每一位的数相加并用一个数组储存每一位数相加和,最终在变量ans(初始化为0)上储存数组中每一位数相加和为m的整数倍的位上置0,不为m整数倍的位上置1,即可得到最终的数。
原理: ==数组一共有a1、a2、……an个数(去重后的),则记ai只出现了l次(l<m),其余的数都出现了m次,则在二进制表现下除了ai的其余数的1位相加得到必定为m的整数倍,由于ai只出现了l次,则最终二进制下所有位按位相加,ai出现1的位上的相加和不能整除m,会余l,因此在该位上ans置1,而相加和为0(所有数该位都为0)以及m的整数倍的位都代表ai在该位上为0,因此在该位上ans置0==。
code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int findsmallnum (int * nums,int m,int len) { int cnt[32 ]={0 }; int i=0 ; for (i;i<len;i++){ int j; for (j=0 ;j<32 ;j++){ cnt[j]+=(nums[i]>>j)&1 ; } } int ans=0 ; for (i=0 ;i<32 ;i++){ if (cnt[i]%m!=0 ){ ans|=(1 <<i); } } return ans; }
二、位运算的骚操作 1.判断一个整数是否为2的幂: 借由Brian Kernighan算法,当n与二进制数只有n最右侧的1的位置上有1的数相同时,n为2的幂,且n要大于0:
code:
1 2 3 int is_twospow (int n) { return n>0 && (n==n&(-n)); }
2.判断一个整数是否为3的幂(int范围内): code: 1 2 3 4 5 6 7 int is_threespow (int n) { return n>0 && 1162261467 %n==0 ; }
3.已知n为非负数,返回大于等于n的最小的2的幂: code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int minPoweroftwo (int n) { if (n<=0 ){ return 1 ; } n--; n|=((unsigned int )n>>1 ); n|=((unsigned int )n>>2 ); n|=((unsigned int )n>>4 ); n|=((unsigned int )n>>8 ); n|=((unsigned int )n>>16 ); return n+1 ; }
4.将区间[left,right]中的数全部&的结果: code: 1 2 3 4 5 6 7 int AllAnd (int left,int right) { while (left<right){ right-=right&(-right); } return right; }
5.将一个数按照其二进制逆序翻转得到新的数: code: 1 2 3 4 5 6 7 8 9 10 11 int reverse (int n) { n=((unsigned int )(n & 0xaaaaaaaa )>>1 ) | ((n & 0x55555555 )<<1 ); n=((unsigned int )(n & 0xcccccccc )>>2 ) | ((n & 0x33333333 )<<2 ); n=((unsigned int )(n & 0xf0f0f0f0 )>>4 ) | ((n & 0x0f0f0f0f )<<4 ); n=((unsigned int )(n & 0xff00ff00 )>>8 ) | ((n & 0x00ff00ff )<<8 ); n=((unsigned int )n>>16 ) | (n<<16 ); return n; }
6.求一个数的二进制表现形式有几个1: code: 1 2 3 4 5 6 7 8 9 10 int searchnumone (int n) { n=(n & 0x55555555 )+((unsigned int )n>>1 & 0x55555555 ); n=(n & 0x33333333 )+((unsigned int )n>>2 & 0x33333333 ); n=(n & 0x0f0f0f0f )+((unsigned int )n>>4 & 0x0f0f0f0f ); n=(n & 0x00ff00ff )+((unsigned int )n>>8 & 0x00ff00ff ); n=(n & 0x0000ffff )+((unsigned int )n>>16 & 0x0000ffff ); return n; }
三、位运算——位图 ### 原理:
用bit组成的数组来存放值,用bit状态1、0代表存在、不存在,取值和存值操作都用位运算。限制是==必须为连续范围且不能过大==,好处是==极大的节省空间==,因为1个数字只占用1个bit的空间。
位图的实现: Bitset(int n):初始化位图的大小,只支持0~n-1所有数字的增删查改
void add(int num):将num加入到位图中
void remove(int num):将num从位图中删除
void reverse(int num):如果位图里没有num,就加入:如果有就删除
int contains(int num):查询num是否在位图中
code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 int *arr=NULL ;int ones;int zeros;int size;bool is_reverse;void Bitset (int n) { arr=(int *)malloc (sizeof (int )*((n+31 )/32 )); int i; for (i=0 ;i<((n+31 )/32 );i++){ arr[i]=0 ; }return ; }void add (int n) { arr[n/32 ] |=1 <<(n%32 ); }void remove0 (int n) { arr[n/32 ] &=~(1 <<(n%32 )); }void reverse (int n) { arr[n/32 ] ^=1 <<(n%32 ); }int contains (int n) { return ((((unsigned int )arr[n/32 ])>>(n%32 ))&1 ==1 ); }void fix (int i) { int index=i/32 ; int rebit=i%32 ; if (!is_reverse){ if ((arr[index] & (1 <<rebit))==0 ){ zeros--; ones++; arr[index] |=(1 <<rebit); } else { if ((arr[index] & (1 <<rebit))!=0 ){ zeros--; ones++; arr[index] ^=(1 <<rebit); } } } return ; }void unfix (int i) { int index=i/32 ; int rebit=i%32 ; if (!is_reverse){ if ((arr[index] & (1 <<rebit))!=0 ){ ones--; zeros++; arr[index] ^=(1 <<rebit); } }else { if ((arr[index] & (1 <<rebit))==0 ){ ones--; zeros++; arr[index] |=(1 <<rebit); } } }void flip () { is_reverse=!is_reverse; ones=ones^zeros; zeros=ones^zeros; ones=ones^zeros; } int isAllone () { return ones==size; }int isleastone () { return (ones>=1 ); }int count () { return ones; }
四、位运算——实现加减乘除: ### 加法:
利用每一步无进位相加即异或运算的结果 + 进位的结果(先作与运算再左移一位,即得到进位信息)不停计算,递归直到进位消失
code: 1 2 3 4 5 6 7 8 9 int oriadd (int x,int y) { int ans=x; while (y!=0 ){ ans=x^y; y=(x&y)<<1 ; x=ans; } return ans; }
减法: code: 1 2 3 4 5 6 int neg (int n) { return oriadd(~n,1 ); } int orisub (int x,int y) { return oriadd(x,neg(y)); }
乘法: 类似于小学的乘法过程
code: 1 2 3 4 5 6 7 8 9 10 11 int orimul (int x,int y) { int ans=0 ; while (y!=0 ){ if (y&1 !=0 ){ ans=oriadd(ans,x); } y=(unsigned int )y>>1 ; x<<=1 ; } return ans; }
除法: 通过一位一位判断x是否含有y乘以2的i次方,来一位一位给ans置1,进而得到答案
code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int oridiv (int x,int y) { int signx=(x<0 ) ? neg(x):x; int signy=(y<0 ) ? neg(y):y; int ans=0 ; int i; for (i=30 ;i>=0 ;i=orisub(i,1 )){ if ((unsigned int )signx>>i>=signy){ signx=orisub(signx,(signy<<i)); ans|=1 <<i; } } return (x>0 )^(y>0 ) ? neg(ans):ans; }
``}``
4.最大频率栈:
栈的压入同一般栈相同,弹出时弹出离栈顶最近,且在栈中频率最大的值。次频表统计值在栈中一共出现的次数并且用一个栈数组来分层存储进入栈的值,加入进来的值存在栈数组中该值压入前在栈中的次数的位置,弹出时弹出最大出现次数的栈顶即可。
code:
`#define Maxsize 3240`
`typedef struct node{`
`int data;`
`struct node * pNext;`
`}NODE,*PNODE;`
`typedef struct stack{`
`PNODE pTop;`
`PNODE pBottom;`
`}STACK,*PSTACK;`
`int maxnum=0;`
`STACK array[Maxsize];//栈数组,用来实现按频率存储以及按频率弹出`
`int foo[Maxsize];//次频表,出现频率,frequency of occurrence`
`void initStack(PSTACK);`
`void push(PSTACK,int);`
`int pop();`
`void initStack(PSTACK pS){`
`pS->pTop=(PNODE)malloc(sizeof(NODE));`
`pS->pBottom=pS->pTop;`
`return ;`
`}`
`void push(PSTACK pS,int val){`
`PNODE pNew=(PNODE)malloc(sizeof(NODE));`
`pNew->data=val;`
`pNew->pNext=pS->pTop;`
`pS->pTop=pNew;`
`return ;`
`}`
`int pop(){`
`if(array[maxnum].pBottom==array[maxnum].pTop){`
`if(maxnum>0){`
`maxnum--;`
`int hold=array[maxnum].pTop->data;`
`PNODE del=array[maxnum].pTop;`
`array[maxnum].pTop=del->pNext;`
`free(del);`
`return hold;`
`}`
`else{`
`return -1;`
`}`
`}`
`else{`
`int hold=array[maxnum].pTop->data;`
`PNODE del=array[maxnum].pTop;`
`array[maxnum].pTop=del->pNext;`
`free(del);`
`return hold;`
`}`
`}`
五、链表高频问题: //用到的链表的结构体定义
1 2 3 4 typedef struct node { int data; struct node *pNext ; }NODE,*PNODE;
1.返回两个无环链表相交的第一个节点,若不相交则返回null 判断两个链表h1、h2(头结点)的走到尾结点e1、e2是否相等,相等即返回返回第一个相交的结点(利用长链表减去两链表长度差值,再同步移动h1、h2,判断h1与h2是否相同,第一个相同的即为第一个相交的节点),不相等返回null。 #### code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 PNODE getIntersec (PNODE h1,PNODE h2) { if (h1==NULL || h2==NULL ){ return NULL ; } PNODE p=h1; PNODE q=h2; PNODE e1,e2; int len1=0 ; int len2=0 ; while (p!=NULL ){ e1=p; p=p->pNext; len1++; } while (q!=NULL ){ e2=q; q=q->pNext; len2++; } if (e1!=e2){ return NULL ; } else { while (len1>len2){ h1=h1->pNext; len1--; } while (len2>len1){ h2=h2->pNext; len2--; } while (h1!=h2){ h1=h1->pNext; h2=h2->pNext; } return h1; } }
### 2.从链表头结点每k个一组翻转链表:
#### code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 PNODE getEnd (PNODE pS,int k) { PNODE p=pS; while (p->pNext!=NULL && --k!=0 ){ p=p->pNext; } return p; }void reverse (PNODE p,PNODE q) { PNODE pre=NULL ; PNODE next=NULL ; PNODE cur=p; PNODE nexth=q->pNext; while (cur!=nexth){ next=cur->pNext; cur->pNext=pre; pre=cur; cur=next; } p->pNext=nexth; return ; } PNODE reverseKgroup (PNODE head,int k) { PNODE start=head; PNODE end=getEnd(start,k); if (end==NULL ){ return head; } head=end; reverse(start,end); PNODE lastGroupEnd=start; while (lastGroupEnd->pNext!=NULL ){ start=lastGroupEnd->pNext; end=getEnd(start,k); if (end==NULL ){ return head; } reverse(start,end); lastGroupEnd->pNext=end; lastGroupEnd=start; } return head; }
### 3.复制带有random指针的链表:
code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 typedef struct node { int data; struct node *pNext ; struct node *random ; }NODE,*PNODE; PNODE initNode (PNODE p) { if (p==NULL ){ p=(PNODE)malloc (sizeof (NODE)); return p; } return p; } PNODE copyList (PNODE head) { if (head==NULL ){ return NULL ; } PNODE cur=head; PNODE next=NULL ; while (cur!=NULL ){ next=cur->pNext; cur->pNext=initNode(cur->pNext); cur->pNext->pNext=next; cur=next; } cur=head; PNODE copy=NULL ; while (cur!=NULL ){ next=cur->pNext->pNext; copy=cur->pNext; copy->random=(cur->random==NULL )?NULL :(cur->random->pNext); cur=next; } PNODE ans=head->pNext; cur=head; while (cur!=NULL ){ next=cur->pNext->pNext; copy=cur->pNext; cur->pNext=next; copy->pNext=(cur->pNext==NULL )?NULL :(next->pNext); cur=next; } return ans; }
### 4.判断链表是否为回文结构:
#### 技巧:
快慢指针求中点:慢指针和快指针一开始都指向头结点,慢指针一次跳一个,快指针一次跳两个,当快指针的next域为NULL或快指针为空时停止,此时慢指针指在近似中点的节点位置 #### code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 int is_Palidrome (PNODE head) { if (head==NULL || head->pNext==NULL ){ return 1 ; } PNODE slow=head; PNODE fast=head; while (fast->pNext!=NULL && fast->pNext->pNext!=NULL ){ slow=slow->pNext; fast=fast->pNext->pNext; } PNODE pre=slow; PNODE cur=pre->pNext; PNODE next=NULL ; pre->pNext=NULL ; while (cur!=NULL ){ next=cur->pNext; cur->pNext=pre; pre=cur; cur=next; } PNODE left=head; PNODE right=pre; int ans=1 ; while (left!=NULL && right!=NULL ){ if (left->data!=right->data){ ans=0 ; break ; } left=left->pNext; right=right->pNext; } cur=pre->pNext; pre->pNext=NULL ; next=NULL ; while (cur!=NULL ){ next=cur->pNext; cur->pNext=pre; pre=cur; cur=next; } return ans; }
5.找链表的第一个入环节点,如果不存在环结构,就返回NULL: code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 PNODE getEntercirnode (PNODE head) { if (head==NULL || head->pNext==NULL || head->pNext->pNext==NULL ){ return NULL ; } PNODE slow=head; PNODE fast=head; while (slow!=fast){ if (fast->pNext==NULL || fast->pNext->pNext==NULL ){ return NULL ; } slow=slow->pNext; fast=fast->pNext->pNext; } fast=head; while (slow!=fast){ slow=slow->pNext; fast=fast->pNext; } return slow; }
6.在链表上排序。要求时间复杂度O(n*logn),额外空间复杂度O(1),还要求排序有稳定性,(在数组层面上不能实现) code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 PNODE getEnd (PNODE pS,int k) { PNODE p=pS; while (p->pNext!=NULL && --k!=0 ){ p=p->pNext; } return p; } PNODE start; PNODE end;void merge (PNODE l1,PNODE r1,PNODE l2,PNODE r2) { PNODE pre=NULL ; if (l1->data<=l2->data){ start=l1; pre=l1; l1=l1->pNext; }else { start=l2; pre=l2; l2=l2->pNext; } while (l1!=NULL && l2!=NULL ){ if (l1->data<=l2->data){ pre->pNext=l1; pre=l1; l1=l1->pNext; }else { pre->pNext=l2; pre=l2; l2=l2->pNext; } } if (l1!=NULL ){ pre->pNext=l1; end=r1; } else { pre->pNext=l2; end=r2; } return ; } PNODE perfectSort (PNODE head) { int len=0 ; PNODE cur=head; while (cur!=NULL ){ len++; cur=cur->pNext; } PNODE l1,r1,l2,r2,next,lastGroupEnd; int step=1 ; for (step;step<len;step<<=1 ){ l1=head; r1=getEnd(l1,step); l2=r1->pNext; r2=getEnd(l2,step); next=r2->pNext; r1->pNext=NULL ; r2->pNext=NULL ; merge(l1,r1,l2,r2); head=start; lastGroupEnd=end; while (next!=NULL ){ l1=next; r1=getEnd(l1,step); l2=r1->pNext; if (l2==NULL ){ lastGroupEnd->pNext=l1; break ; } r2=getEnd(l2,step); next=r2->pNext; r1->pNext=NULL ; r2->pNext=NULL ; merge(l1,r1,l2,r2); lastGroupEnd->pNext=start; lastGroupEnd=end; } } return head; }
六、数据结构设计高频题: ### 1.LRU缓存:
最近最少使用缓存约束的数据结构
使用哈希表和双向链表实现
2.插入、删除、元素等概率随机获取时间复杂度均为O(1)的结构: 哈希map记录加入值和其在动态数组中的下标,随机获取时在哈希map中随机获取下标,得到相应的随机值
删除操作时用最后一个数去填删除的数来保证下标的连续,保证删除之后随机获取的随机性
若允许重复数字的加入,那可以采用哈希map中value采用集合的形式存储key在动态数组中一共出现过的下标,相应的remove时再删除的值同动态数组的最后一个值不同时,用动态数组中最后一个数值,来到删除位置,并在哈希mapvalue集合中进行下标的删除和插入;相同时,删除最后一个值即可。若remove后该值value集合已为空,那么应该在哈希map里删除该键值对。
3.快速获取数据流的中位数: 堆实现:大根堆加上小根堆,希望较小的一半都在大根堆里,而较大的一半都在小根堆里。若两个堆都为空,新来数字进大根堆。若至少一个不为空,则新来数字若小于等于大根堆的顶,其进入大根堆,反之进入小根堆。大小根堆大小差值大于等于2时,较小的一方应弹出堆顶进入另一个堆,两个堆大小之和为偶数时,中位数为两个堆堆顶数的平均数;为奇数时,中位数为大小为偶数一方的堆顶。 #### code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 #define Maxsize 3240 int bigHeap[Maxsize];int smallHeap[Maxsize];int bigSize;int smallSize;double midnum;void swap (int *array ,int i,int j) { int temp=array [i]; array [i]=array [j]; array [j]=temp; return ; } void heapInsert (int *array ,int i,int key) { if (key==1 ){ while (array [i]>array [(i-1 )/2 ]){ swap(bigHeap,i,(i-1 )/2 ); i=(i-1 )/2 ; } }if (key==2 ){ while (array [i]<array [(i-1 )/2 ]){ swap(smallHeap,i,(i-1 )/2 ); i=(i-1 )/2 ; } } return ; }void heapAdjust (int *array ,int i,int key) { if (key==1 ){ int hold=i*2 +1 ; while (hold<bigSize){ int bigger=(hold+1 <bigSize && array [hold+1 ]>array [hold])?(hold+1 ):hold; bigger=array [bigger]>array [i]?bigger:i; if (bigger==i){ heapInsert(bigHeap,i,1 ); return ; } swap(bigHeap,i,bigger); i=bigger; hold=i*2 +1 ; } }if (key==2 ){ int hold=i*2 +1 ; while (hold<smallSize){ int smaller=(hold+1 <smallSize && array [hold+1 ]<array [hold])?(hold+1 ):hold; smaller=array [smaller]<array [i]?smaller:i; if (smaller==i){ heapInsert(smallHeap,i,2 ); return ; } swap(smallHeap,i,smaller); i=smaller; hold=i*2 +1 ; } } return ; }int pop (int *array ,int size,int key) { int hold=array [0 ]; swap(array ,0 ,size-1 ); size--; heapAdjust(array ,0 ,key); return hold; }int main () { int n; scanf ("%d" ,&n); int i=0 ; int x; for (i;i<n;i++){ scanf ("%d" ,&x); if (bigSize==0 && smallSize==0 ){ bigHeap[bigSize]=x; heapInsert(bigHeap,bigSize++,1 ); midnum=x; }else { if (x<=bigHeap[0 ]){ bigHeap[bigSize]=x; heapInsert(bigHeap,bigSize++,1 ); }else { smallHeap[smallSize]=x; heapInsert(smallHeap,smallSize++,2 ); } if (bigSize-smallSize>=2 ){ int temp=pop(bigHeap,bigSize,1 ); smallHeap[smallSize]=temp; heapInsert(smallHeap,smallSize++,2 ); } if (smallSize-bigSize>=2 ){ int tempp=pop(smallHeap,smallSize,2 ); bigHeap[bigSize]=tempp; heapInsert(bigHeap,bigSize++,1 ); } if ((bigSize+smallSize)%2 ==0 ){ midnum=(double )(bigHeap[0 ]+smallHeap[0 ])/2 ; } else { midnum=(bigSize>smallSize)?bigHeap[0 ]:smallHeap[0 ]; } } printf ("%lf " ,midnum); } return 0 ; }
4.最大频率栈: 栈的压入同一般栈相同,弹出时弹出离栈顶最近,且在栈中频率最大的值。次频表统计值在栈中一共出现的次数并且用一个栈数组来分层存储进入栈的值,加入进来的值存在栈数组中该值压入前在栈中的次数的位置,弹出时弹出最大出现次数的栈顶即可。
code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #define Maxsize 3240 typedef struct node { int data; struct node * pNext ; }NODE,*PNODE;typedef struct stack { PNODE pTop; PNODE pBottom; }STACK,*PSTACK;int maxnum=0 ; STACK array [Maxsize];int foo[Maxsize];void initStack (PSTACK) ;void push (PSTACK,int ) ;int pop () ;void initStack (PSTACK pS) { pS->pTop=(PNODE)malloc (sizeof (NODE)); pS->pBottom=pS->pTop; return ; }void push (PSTACK pS,int val) { PNODE pNew=(PNODE)malloc (sizeof (NODE)); pNew->data=val; pNew->pNext=pS->pTop; pS->pTop=pNew; return ; }int pop () { if (array [maxnum].pBottom==array [maxnum].pTop){ if (maxnum>0 ){ maxnum--; int hold=array [maxnum].pTop->data; PNODE del=array [maxnum].pTop; array [maxnum].pTop=del->pNext; free (del); return hold; } else { return -1 ; } } else { int hold=array [maxnum].pTop->data; PNODE del=array [maxnum].pTop; array [maxnum].pTop=del->pNext; free (del); return hold; } }
七、二叉树高频题: 1.bfs的两种实现方式: ①队列加哈希表(表记录弹出结点的层数),根据哈希表中的数值与对应层数的关系在链表数组中进行处理。 ②一次处理一层的方式: 1)拿当前队列的长度size 2)如下行为重复size遍: (1)弹出队首结点放入其所在层的链表中。 (2)有左节点加左节点;有右节点加右节点 code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #define Maxsize 3240 typedef struct btnode { int data; struct btnode *left ,*right ; }BTNODE,*PBTNODE;typedef struct list { int length; int array [Maxsize]; }LIST,*PLIST;int l;int r;int count; BTNODE queue [Maxsize]; PLIST linkList[Maxsize]; PBTNODE initBTree (PBTNODE pT,int val) { if (pT==NULL ){ pT=(PBTNODE)malloc (sizeof (BTNODE)); pT->data=val; pT->left=NULL ; pT->right=NULL ; }else { if (pT->data>=val){ pT->left=initBTree(pT->left,val); }else { pT->right=initBTree(pT->right,val); } } return pT; } void bfs (PBTNODE pRoot) { if (pRoot!=NULL ){ l=r=0 ; queue [r++]=*pRoot; count=1 ; while (l<r){ int size=r-l; int i=0 ; for (i;i<size;i++){ if (i==0 ){ linkList[count]=(PLIST)malloc (sizeof (LIST)); } BTNODE hold=queue [l++]; printf ("%d " ,hold.data); linkList[count]->array [i]=hold.data; if (hold.left!=NULL ){ queue [r++]=*hold.left; } if (hold.right!=NULL ){ queue [r++]=*hold.right; } } linkList[count]->length=size; count++; } } return ; }int main () { int n; PBTNODE PROOT=NULL ; scanf ("%d" ,&n); int i,x; for (i=0 ;i<n;i++){ scanf ("%d" ,&x); PROOT=initBTree(PROOT,x); } bfs(PROOT); printf ("\n" ); int j; for (i=1 ;i<count;i++){ for (j=0 ;j<linkList[i]->length;j++){ printf ("%d " ,linkList[i]->array [j]); } } return 0 ; }
若要求锯齿状层次遍历二叉树,即相邻的两层的遍历左右次序不同,则队列加入结点的方式同上,弹出结点的方式改为如下: ##### code:
1 2 3 4 5 6 7 8 9 10 bool reverse=false ;int i,j,k;for (i=(reverse)?(r-1 ):l,j=(reverse)?(-1 ):1 ,k=0 ;k<size;i+=j,k++){ BTNODE hold=queue [i]; linkList[count]->array [i]=hold.data; } reverse=!reverse;
2.最大特殊宽度: code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #define Maxsize 3240 typedef struct btnode { int data; struct btnode *left ,*right ; }BTNODE,*PBTNODE;int l;int r;int ans; BTNODE nodeQueue[Maxsize];int idQueue[Maxsize]; PBTNODE initBTree (PBTNODE pT,int val) { if (pT==NULL ){ pT=(PBTNODE)malloc (sizeof (BTNODE)); pT->data=val; pT->left=NULL ; pT->right=NULL ; }else { if (pT->data>=val){ pT->left=initBTree(pT->left,val); }else { pT->right=initBTree(pT->right,val); } } return pT; } void bfsOFfindbigspecialwidth (PBTNODE pT) { ans=1 ; l=r=0 ; nodeQueue[r]=*pT; idQueue[r++]=1 ; while (l<r){ int size=r-l; ans=(ans>(idQueue[r-1 ]-idQueue[l]+1 ))?ans:(idQueue[r-1 ]-idQueue[l]+1 ); int i=0 ; for (i;i<size;i++){ BTNODE hold=nodeQueue[l]; printf ("%d " ,hold.data); int id=idQueue[l++]; if (hold.left!=NULL ){ nodeQueue[r]=*hold.left; idQueue[r++]=id*2 ; } if (hold.right!=NULL ){ nodeQueue[r]=*hold.right; idQueue[r++]=id*2 +1 ; } } } }
### 3.二叉树的最小高度(从头结点到所有叶节点的高度中的最小值):
code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #define Maxsize 3240 typedef struct btnode { int data; struct btnode *left ,*right ; }BTNODE,*PBTNODE;int minDepth (PBTNODE pRoot) { if (pRoot==NULL ){ return 0 ; } if (pRoot->left==NULL && pRoot->right==NULL ){ return 1 ; } int ldeep=Maxsize; int rdeep=Maxsize; if (pRoot->left!=NULL ){ ldeep=minDepth(pRoot->left); } if (pRoot->right!=NULL ){ rdeep=minDepth(pRoot->right); } return ((ldeep<=rdeep)?ldeep:rdeep)+1 ; }
4.二叉树的先序序列化和反序列化 要求将一棵二叉树的按照序列遍历转换成一串字符串,其中空结点由占位符”#”占用,再提供反序列化的方法。
code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #define MAX_SIZE 1000 int count, index;char preOrderStr[MAX_SIZE];typedef struct BTNode { char val; struct BTNode * left ; struct BTNode * right ; } BTNode, *PBTNode; PBTNode createNode (PBTNode root, char val) { if (root == NULL ) { PBTNode root = (PBTNode)malloc (sizeof (BTNode)); root->val = val; root->left = NULL ; root->right = NULL ; return root; } else { if (root->val >= val) { root->left = createNode(root->left, val); } else { root->right = createNode(root->right, val); } } } void preOrder (PBTNode root) { if (root == NULL ) { preOrderStr[count++] = '#' ; return ; } preOrderStr[count++] = root->val; preOrder(root->left); preOrder(root->right); } PBTNode deserialize (PBTNode root) { if (preOrderStr[index] == '#' ) { index++; return NULL ; } PBTNode newNode = (PBTNode)malloc (sizeof (BTNode)); newNode->val = preOrderStr[index++]; newNode->left = deserialize(newNode->left); newNode->right = deserialize(newNode->right); return newNode; }int main () { PBTNode root = NULL ; char val; int i; int n; printf ("Enter the number of nodes: " ); scanf ("%d" , &n); for (i = 0 ; i < n; i++) { printf ("Enter the value of node %d: " , i+1 ); scanf (" %c" , &val); root = createNode(root, val); } preOrder(root); printf ("%s\n" , preOrderStr); PBTNode newRoot = NULL ; newRoot = deserialize(newRoot); count = 0 ; preOrder(newRoot); printf ("%s\n" , preOrderStr); return 0 ; }
5.二叉树的按层次序列化和反序列化 给出树的根节点或者给出建树的方式
code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 void bfs_order_serialize (PBTNode root) { if (root == NULL ) { bfs_order[cnt++] = '#' ; return ; } int front = 0 ; int rear = 0 ; queue [rear++] = *root; bfs_order[cnt++] = root->val; while (front < rear) { BTNode temp = queue [front++]; if (temp.left != NULL ) { queue [rear++] = *temp.left; bfs_order[cnt++] = temp.left->val; } else { bfs_order[cnt++] = '#' ; } if (temp.right != NULL ) { queue [rear++] = *temp.right; bfs_order[cnt++] = temp.right->val; } else { bfs_order[cnt++] = '#' ; } } return ; } PBTNode bfs_order_deserialize (PBTNode root, int index) { if (bfs_order[index - 1 ] == '#' ){ return NULL ; } PBTNode newNode = (PBTNode)malloc (sizeof (BTNode)); newNode->val = bfs_order[index - 1 ]; newNode->left = bfs_order_deserialize(root, 2 * index); newNode->right = bfs_order_deserialize(root, 2 * index + 1 ); return newNode; }
6.利用二叉树的先序序列和中序序列建树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 #define MAX_SIZE 100 typedef struct BTNode { int data; struct BTNode * left ; struct BTNode * right ; } BTNode, *PBTNode;typedef struct node { int data; int num; struct node * next ; } NODE, *Pnode; Pnode hashlinkedlist[13 ];char preorder[MAX_SIZE];char inorder[MAX_SIZE];int int_preorder[MAX_SIZE];int int_inorder[MAX_SIZE];int count;void createHashLinkedList (char *str) { int i; for (i = 0 ; i < 13 ; i++) { hashlinkedlist[i] = NULL ; } int len = strlen (str); int cur = 0 ; for (i = 0 ; i < len; i++) { if (str[i] >= '0' && str[i] <= '9' ) { cur = cur * 10 + (str[i] - '0' ); } else { if (hashlinkedlist[cur % 13 ] == NULL ) { Pnode PNode = (Pnode)malloc (sizeof (NODE)); PNode->data = cur; PNode->num = count; PNode->next = NULL ; hashlinkedlist[cur % 13 ] = PNode; } else { Pnode temp = hashlinkedlist[cur % 13 ]; while (temp != NULL ) { temp = temp->next; } temp->num = count; temp->data = cur; temp->next = NULL ; } count++; cur = 0 ; } } if (hashlinkedlist[cur % 13 ] == NULL ) { Pnode PNode = (Pnode)malloc (sizeof (NODE)); PNode->data = cur; PNode->num = count; PNode->next = NULL ; hashlinkedlist[cur % 13 ] = PNode; } else { Pnode temp = hashlinkedlist[cur % 13 ]; while (temp != NULL ) { temp = temp->next; } temp->num = count; temp->data = cur; temp->next = NULL ; } count++; return ; }void adjust (char * str, int * int_str, int len) { int cur = 0 ; int cnt = 0 ; int i; for (i = 0 ; i < len; i++) { if (str[i] >= '0' && str[i] <= '9' ) { cur = cur * 10 + (str[i] - '0' ); } else { int_str[cnt++] = cur; cur = 0 ; } } int_str[cnt++] = cur; }int fun (int c) { Pnode temp = hashlinkedlist[c % 13 ]; while (temp->data != c) { temp = temp->next; } return temp->num; } PBTNode createBT (int * int_preorder, int * int_inorder, int pre_start, int pre_end, int in_start, int in_end) { if (pre_start > pre_end) { return NULL ; } PBTNode pnewNode = (PBTNode)malloc (sizeof (BTNode)); pnewNode->data = int_preorder[pre_start]; int find = fun(int_preorder[pre_start]); pnewNode->left = createBT(int_preorder, int_inorder, pre_start + 1 , pre_start + find - in_start, in_start, find - 1 ); pnewNode->right = createBT(int_preorder, int_inorder, pre_start + find - in_start + 1 , pre_end, find + 1 , in_end); return pnewNode; }
7.判断完全二叉树 利用按层次遍历 的方式,一次遍历每一层,对于每一个结点,如果有一个结点只有右孩子,但无左孩子 ,则该树必不为完全二叉树;若有一个结点孩子不全,则接下来的所有遍历的结点必须全为叶子节点 ,否则也不为完全二叉树。
8.计算完全二叉树的结点个数,要求复杂度小于O(n) code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int mostleftdown (PBTNode PNode, int level) { while (PNode != NULL ) { PNode = PNode->left; level++; } return level - 1 ; }int function (PBTNode PNode, int level, int h) { if (level == h) { return 1 ; } if (mostleftdown(PNode->right, level + 1 ) == h) { return ((1 << (h - level)) + function(PNode->right, level + 1 , h)); } else { return ((1 << (h - level - 1 )) + function(PNode->left, level + 1 , h)); } }int count_complete_binary_trees (PBTNode root) { if (root == NULL ) { return 0 ; } return function(root, 1 , mostleftdown(root, 1 )); }
9.找一颗普通二叉树中两个不同结点的最近相同的祖先节点