首页 > 代码库 > 笔试算法题(24):找出出现次数超过一半的元素 & 二叉树最近公共父节点

笔试算法题(24):找出出现次数超过一半的元素 & 二叉树最近公共父节点

出题:数组中有一个数字出现的次数超过了数组长度的一半,请找出这个数字;

分析:

  • 解法1:首先对数组进行排序,时间复杂度为O(NlogN),由于有一个数字出现次数超过了数组的一半,所以如果二分数组的话,划分元素肯定就是这个数字;
  • 解法2:首先创建1/2数组大小的Hash Table(哈希表可以替代排序时间,由于一个数字出现超过了数组的一半,所以不同元素个数肯定不大于数组的一半),空间复杂度O(N),顺序扫描映射数 组元素到Hash Table中并计数,最后顺序扫描Hash Table,计数超过数组大小一半的元素就是这个数字,时间复杂度O(N);
  • 解法3:由于一个数字的出现次数超过了数组的一半,所以这个数字的次数减去其他数字的总和的值仍旧大于0;定义两个变量,一个存储数字A,一个进行计数 C;当下一个数字与A相同,则C+1,当下一个数字与A不同,则C-1;当C等于0的时候,A初始化为下一个数字。最终A就是这个数字,时间复杂度 O(N);
  • 扩展问题:如果有三个数字出现的次数均超过了数字总数的1/4,则如何快速找到这三个数字。同样使用解法3的思想,同时删除4个不同的数字,直到找不到4个不同的数字,剩下的数字就是由三个不同数字重复出现组成的。时间复杂度O(N);

解题:

 1 int HalfElementArray(int *array, int length) {
 2         int num=array[0];
 3         int count=1;
 4 
 5         for(int i=1;i<length;i++) {
 6                 if(array[i]==num)
 7                         count++;
 8                 else {
 9                         if(count==0)
10                                 num=array[i+1];
11                         else
12                                 count--;
13                 }
14         }
15         return num;
16 }
17 void QuarterElementsArray(int *array, int length) {
18         int num1=array[0],num2=array[1],num3=array[2];
19         int count1=0,count2=0,count3=0;
20 
21         for(int i=3;i<length;i++) {
22                 if(array[i]==num1)
23                         count1++;
24                 else if(array[i]==num2)
25                         count2++;
26                 else if(array[i]==num3)
27                         coun3++;
28                 else {
29                         if(count1==0)
30                                 num1=array[i+1];
31                         else if(count2==0)
32                                 num2=array[i+1];
33                         else if(count3==0)
34                                 num3=array[i+1];
35                         else {
36                                 num1--;num2--;num3--;
37                         }
38                 }
39                 printf("%D, %d, %d\n",num1,num2,num3);
40         }
41 }

 

出题:给定一棵二叉树,以及两个节点,要求找到两个节点最近的公共父节点;

分析:

  • 解法1:遍历整棵树分别查找两个节点,并分别使用两个Stack(Queue)结构保存根节点到查找节点的路径,然后查找路径中的第一个公共节点(最后一个公共节点),时间复杂度为O(N),空间复杂度为O(logN);
  • 解法2:使用递归方法,从当前节点的左右子树中确定是否包含两个节点A和B,如果A和B都在左子树中则递归,如果A和B都在右子树中则递归,如果一个在左 子树一个在右子树则当前节点就是第一个公共节点,由于每次都需要判断子树是否包含某个节点,所以时间复杂度为O(N^2),没有额外的空间复杂度;

解题:

  1 struct Node {
  2         int value;
  3         Node *left;
  4         Node *right;
  5 };
  6 
  7 /**
  8  * 使用stack结构保存root到target节点的路径的解法
  9  * 算法复杂度为O(N),空间复杂度为O(NlogN)
 10  * */
 11 
 12 /**
 13  * stack实现
 14  * */
 15 class MyStack {
 16 private:
 17         Node **array;
 18         int capability;
 19         int top;
 20 public:
 21         MyStack(int cap=5): array((Node**)malloc(sizeof(Node)*cap)), capability(cap), top(0) {}
 22         ~MyStack() {delete [] array;}
 23 
 24         bool isFull() {
 25                 return top == capability;
 26         }
 27         bool isEmpty() {
 28                 return top == 0;
 29         }
 30         int freeSlot() {
 31                 return capability - top;
 32         }
 33 
 34         /**
 35          * top当前的位置就是下一个push元素所在的slot
 36          * */
 37         bool push(Node *n) {
 38                 if(isFull()) return false;
 39                 array[top++]=n;
 40                 return true;
 41         }
 42         bool pop(Node **n) {
 43                 if(isEmpty()) return false;
 44                 *n=array[--top];
 45                 return true;
 46         }
 47         void ShowStack() {
 48                 int temp=top-1;
 49                 printf("\n");
 50                 for(int i=0;i<=temp;i++)
 51                         printf("%d, ",array[i]->value);
 52                 printf("\n");
 53         }
 54 };
 55 
 56 Node* GetLowerFather(MyStack *sfirst, MyStack *ssecond) {
 57 
 58 }
 59 
 60 bool FindTargetPath(Node *root, Node *target, MyStack *mstack) {
 61         if(root==NULL)
 62                 return false;
 63         mstack->push(root);
 64 
 65         if(root==target)
 66                 return true;
 67         else if(FindTargetPath(root->left,target, mstack))
 68                 return true;
 69         else if(FindTargetPath(root->right,target, mstack))
 70                 return true;
 71         else {
 72                 mstack->pop(NULL);
 73                 return false;
 74         }
 75 }
 76 
 77 Node* FindCommonFather1(Node *root, Node *first, Node *second) {
 78         if(first==NULL || second==NULL)
 79                 return NULL;
 80         MyStack *sfirst=new MyStack(10);
 81         MyStack *ssecond=new MyStack(10);
 82 
 83         if(FindTargetPath(root, first, sfirst) &&
 84                         FindTargetPath(root, second, ssecond))
 85                 return GetLowerFather(sfirst, ssecond);
 86         else {
 87                 printf("\nbad input");
 88                 return NULL;
 89         }
 90 }
 91 
 92 /**
 93  * 直接使用递归的解法
 94  * 算法复杂度为O(N^2)
 95  * */
 96 
 97 bool HasNode(Node *root, Node *target) {
 98         if(root==NULL) return false;
 99         if(root==target) return true;
100         /**
101          * 仅当左子树没有找到target的时候,才处理右子树
102          * */
103         bool bleft=HasNode(root->left,target);
104         if(!bleft) {
105                 return HasNode(root->right,target);
106         } else {
107                 return true;
108         }
109 }
110 
111 Node* FindCommonFather(Node *cur, Node *first, Node *second) {
112         /**
113          * 如果当前节点为NULL,返回NULL
114          * */
115         if(cur == NULL) return NULL;
116         /**
117          * 判断是否first和second都在左子树
118          * 注意处理first和second为同一个节点的情况
119          * 注意处理first和second中一个节点是另一个节点的父节点的情况
120          * */
121         bool bfirstleft=false, bsecondleft=false;
122         if(cur->left != NULL) {
123                 bfirstleft=HasNode(cur->left,first);
124                 bsecondleft=HasNode(cur->left,second);
125                 if(bfirstleft && bsecondleft) {
126                         /**
127                          * 这里的||操作可以处理当一个节点是另一个节点的
128                          * 父节点的情况
129                          * */
130                         if(cur->left==first || cur->left==second)
131                                 return cur;
132                         else
133                                 return FindCommonFather(cur->left,first,second);
134                 }
135         }
136         /**
137          * 判断是否first和second都在右子树
138          * 注意处理first和second为同一个节点的情况
139          * 注意处理first和second中一个节点是另一个节点的父节点的情况
140          * */
141         bool bfirstright=false, bsecondright=false;
142         if(cur->right != NULL) {
143                 bfirstright=HasNode(cur->right,first);
144                 bsecondright=HasNode(cur->right,second);
145                 if(bfirstright && bsecondright) {
146                         /**
147                          * 这里的||操作可以处理当一个节点是另一个节点的
148                          * 父节点的情况
149                          * */
150                         if(cur->right==first || cur->right==second)
151                                 return cur;
152                         else
153                                 return FindCommonFather(cur->right,first,second);
154                 }
155         }
156         /**
157          * 判断是否first在左且second在右,或者first在右且
158          * second在左
159          * */
160         if((bfirstleft && bsecondright) ||
161                         (bfirstright && bsecondleft))
162                 return cur;
163         return NULL;
164 }