出题:数组中有一个数字出现的次数超过了数组长度的一半,请找出这个数字;
分析:
- 解法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 ",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(" "); 50 for(int i=0;i<=temp;i++) 51 printf("%d, ",array[i]->value); 52 printf(" "); 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(" bad 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 }