1.
写一个程序, 要求功能:求出用1,2,5这三个数不同个数组合的和为100的组合个数。
如:100个1是一个组合,5个1加19个5是一个组合。。。。 请用C++语言写。
解决方案:
普通方法大家都会:
int main(){ int cnt = 0; for(int i = 0; i <= 100; i ++){ for(int j = 0; j <= 50; j ++){ for(int k = 0; k <= 20; k ++){ if(i + 2 * j + 5 * k == 100) cnt ++; } } } printf("%d ", cnt); return 0; }
但是这循环是101*51*21次,复杂度大
由题知:假设X为1的个数,Y为2的个数,Z为5的个数,那么满足X+2*Y+5*Z = 100
所以X+5*Z = 100 - 2*Y,很明显X+5*Z是一个偶数,并且Z是<=20的,那么对Z做循环
Z=0, X=100,98,...,0
Z=1,X=95,93,...,1
Z=2,X=90,88,...0
.
.
.
Z=19,X=5,3,1
Z=20,X=0
所以结果只要求出100以内的偶数,95以内的奇数...最后是5以内的奇数再加一次就行了
对于一个奇数N求0到N之间的奇数个数为N/2+1,同样的偶数个数为N/2+1
代码如下:
int main(){ int sum = 0; for(int i = 0; i <= 100; i += 5){ sum += (100 - i) / 2 + 1; } printf("%d ", sum); return 0; }
2.
写一个链表,删除一个结构体中指定的结点
解决方案:
#include <cstdio> #include <cstring> #include <cstdlib> struct stu{ int age; stu *next; }*list; struct stu *creatlist(int n){ stu *h, *p, *s; h = new stu; h->next = NULL; p = h; for(int i = 0; i < n; i ++){ s = new stu; p->next = s; scanf("%d", &s->age); s->next = NULL; p = s; } return h; } void output(stu *s){ s = s->next; while(s != NULL){ printf("%d ", s->age); s = s->next; } puts(""); } void deletelist(stu *h, int age){ stu *p = h; stu *s = h->next; while(s != NULL){ if(s->age == age){ p->next = s->next; s = s->next; } else{ p = p->next; s = s->next; } } } int main(){ int n; scanf("%d", &n); stu *s = creatlist(n); //output(s->next); int age; scanf("%d", &age); deletelist(s, age); output(s); return 0; }
3.
将二叉树的两个孩子换位置,即左变右,右变左。不能用递规
解决方案:
1)根结点入队列
2)取队首元素并出列,并将该元素左右儿子进行交换
3)如果队列不为空则跳到步骤2
#include <cstdio> #include <cstring> #include <cstdlib> #include <queue> using namespace std; struct tree{ tree *right; tree *left; }*list; void solve(tree *list){ queue <tree*> Q; Q.push(list); while(!Q.empty()){ tree *tmp = Q.front(); Q.pop(); swap(tmp->right, tmp->left); if(tmp->right != NULL) Q.push(tmp->right); if(tmp->left != NULL) Q.push(tmp->left); } }
4.
38头牛中选出3头跑得最快的,使用一个每次只能供6头比赛的场地,要求用最快的方法
解决方案:
38只取6只一组,6*6,取前3名,得3*6+2=20,用了6次
20只取5只一组,取前3名,得到3*4=12,用了4次
12只分两组取前3名,得3*2=6,用了2次
最后再加1次
累计13次
5.
n从1开始,每个操作可以选择对n加1或者对n加倍。若想获得整数2013,最少需要多少个操作
解决方案:
直接进行计算,奇数减1除2,偶数直接除以2,2013>1006>503>251>125>62>31>15>7>3>1
再反过来计算从1到2013的次数,为18次
6.
如下函数,在32bit系统foo(2^31-3)的值是:
Int foo(int x)
{
Return x&-x;
}
解决方案:
这题是先计算31-3再计算2异或28的,然后进行 x & -x
对于x & -x的含义:返回值为0,表示x=0;返回值为1,表示x为奇数;返回值为其他数,表示x为x与2^64的最大公约数,即二进制中从右到左的第一个1出现的位数
扩展:x & (x-1) 表示x进进制中1的个数,每执行一次x = x&(x-1),会将x用二进制表示时最右边的一个1变为0,因为x-1将会将该位(x用二进制表示时最右边的一个1)变为0
所以可通过这个求得一个数是不是2的n次方,代码如下
int func(int x) { if( (x&(x-1)) == 0 ) return 1; else return 0; } int main() { int x = 8; printf("%d ", func(x)); }
7.
给两个单向链表,求是否有公共节点并求该公共节点,要求使用空间最小算法
解决方案:
将链表A遍历以及链表B遍历,分别保存最后一个节点以及他们长度,若相同再证明有公共节点
如果他们长度相等,依次遍历就行,如果长度不等,将长度长的先遍历|len_a - len_b|(表示绝对值)
如图
9.
static静态在申请的时候不分配内存
struct A{ A() {} ~A() {} int m1; int m2; }; struct B{ B() {} ~B() {} int m1; char m2; static char m3; }; struct C{ C() {} virtual~C() {} int m1; short m2; }; int main(){ printf("%d %d %d ", sizeof A, sizeof B, sizeof C); return 0; }
输出的是:8 8 12
10.
定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。要求函数min、push以及pop的时间复杂度都是O(1)
解决方案:这题难点在于pop一个元素之后,怎样确定栈中的最小元素,然后查找就要遍历显示不行,
那么就设定一个辅助栈,将每次数据栈中最小的元素依次入栈,见代码(考虑整型)
#include <cstdio> #include <cstring> #include <stack> using namespace std; class Stack { public: void push(int num){ data_stack.push(num); if(!min_stack.size()) min_stack.push(num); else if(num <= min_stack.top()) min_stack.push(num); } void pop(){ if(data_stack.top() == min_stack.top()) min_stack.pop(); data_stack.pop(); } void output(){ printf("%d ", min_stack.top()); } protected: private: stack <int > data_stack; stack <int > min_stack; }; int main(){ Stack s; s.push(3); s.output(); s.push(4); s.output(); s.push(2); s.output(); s.push(1); s.output(); s.pop(); s.output(); s.pop(); s.output(); s.push(0); s.output(); return 0; }
11.
求最大子和段
解决方案:从第一个数开始加,如果碰到和为负数则置0,否则和max值比较取最大值
#include <cstdio> #include <cstring> #include <stack> using namespace std; int a[8] = {1, -2, 3, 10, -4, 7, 2, -5}; int main(){ int sum = a[0], max = a[0]; for(int i = 1; i < 8; i ++){ if(sum + a[i] < 0) sum = 0; else sum += a[i]; if(max < sum) max = sum; } printf("%d ", max); return 0; }
12.
查找最小的k个元素
解决方案:
朴素方案或者遍历选择最小K个或者快速排序选择前K个,复杂度略高
可以先插入K个到数组中,然后每插入一个数和前K个比较,比最大的小才插入并删除最大的
如何做到在K个当中查找最大的,可以用一个最大堆,或者用第10题两个栈的做法(个人认为可以,只是弹栈的要换一种方式了)
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> using namespace std; multiset <int > m; int main(){ m.clear(); m.insert(3); m.insert(5); m.insert(4); m.insert(1); m.insert(7); m.insert(8); m.insert(2); m.insert(6); for(multiset<int > :: iterator it= m.begin(); it != m.end(); it ++){ cout<<*it<<endl; } return 0; }
参考别人的
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> using namespace std; const int k = 5; const int maxn = 1000; int max_heap[k + 1]; int ed, maxpos; void insert_min_heap(int data){ int child = 0; if(ed == k + 1){ if(data >= max_heap[1]) return; max_heap[1] = data; for(int i = 1; i * 2 <= k; i = child){ child = 2 * i; if((i * 2 + 1 <= k && max_heap[i*2] < max_heap[i*2+1])){ child ++; } if(max_heap[i] < max_heap[child]) swap(max_heap[i], max_heap[child]); else break; } return; } max_heap[ed ++] = data; for(int i = ed - 1; i > 1; i /= 2){ if(max_heap[i] > max_heap[i/2]) swap(max_heap[i], max_heap[i/2]); else break; } } int main(){ int n, data; ed = 1; while(~scanf("%d", &data)){ insert_min_heap(data); } for(int i = 1; i <= k; i ++) printf("%d ", max_heap[i]); puts(""); return 0; }
13.
写一个堆排序
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> using namespace std; int n, a[100]; void init_heap(){ for(int i = n; i; i --){ int cur = i; while(true){ int next = -1; if((cur * 2 <= n) && (next < 0 || a[cur*2] > a[next])) next = cur * 2; if((cur * 2 + 1 <= n) && (a[cur*2+1] > a[next])) next = cur * 2 + 1; if(next < 0 || a[next] <= a[cur]) break; else{ swap(a[cur], a[next]); cur = next; } } } } void heap_sort(){ for(int i = n; i > 1; i --){ swap(a[1], a[i]); int cur = 1; while(true){ int next = -1; if((cur * 2 < i) && (next < 0 || a[cur*2] > a[next])) next = cur * 2; if((cur * 2 + 1 < i) && (a[cur*2+1] > a[next])) next = cur * 2 + 1; if(next < 0 || a[next] <= a[cur]) break; else{ swap(a[cur], a[next]); cur = next; } } } } int main(){ scanf("%d ", &n); for(int i = 1; i <= n; i ++) scanf("%d", a + i); init_heap(); heap_sort(); for(int i = 1; i <= n; i ++){ printf("%d%c", a[i], i < n ? ' ' : ' '); } }
14.
前序中序后序遍历,创建二叉树时采用队列实现层序遍历创建
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <queue> using namespace std; struct Node{ int num; Node *l, *r; }; queue <Node * > Q; void creat_tree(Node *p, int num, int &f){ Node *s = new Node; s->num = num; s->l = s->r = NULL; if(f == 1){ p->l = s; f = 2; Q.push(s); return; } p->r = s; f = 1; Q.push(s); Q.pop(); } void dlr_output(Node *p){ if(p == NULL) return; printf("%d ", p->num); dlr_output(p->l); dlr_output(p->r); } void ldr_output(Node *p){ if(p == NULL) return; ldr_output(p->l); printf("%d ", p->num); ldr_output(p->r); } void lrd_output(Node *p){ if(p == NULL) return; lrd_output(p->l); lrd_output(p->r); printf("%d ", p->num); } int main(){ Node *root = new Node; Node *p = new Node; int f = 0; int num; while(!Q.empty()) Q.pop();//清空队列 while(~scanf("%d", &num)){ if(f == 0){ root->num = num; root->l = NULL; root->r = NULL; f = 1; p = root; Q.push(p); continue; } p = Q.front(); creat_tree(p, num, f); } dlr_output(root);puts(""); ldr_output(root);puts(""); lrd_output(root);puts(""); return 0; }
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <string> #include <queue> using namespace std; struct TreeNode{ int value; TreeNode *left; TreeNode *right; }; TreeNode *creatTreeNode(int m){ TreeNode *node = new TreeNode; node->value = m; node->right = node->left = NULL; return node; } void DestroyTree(TreeNode *root){ if(root != NULL){ TreeNode *l = root->left; TreeNode *r = root->right; delete root; root = NULL; DestroyTree(l); DestroyTree(r); } } void connectTree(TreeNode *root, TreeNode *l, TreeNode *r){ if(root != NULL){ root->left = l; root->right = r; } } void solve(TreeNode *root); int main(){ TreeNode *node1 = creatTreeNode(1); TreeNode *node2 = creatTreeNode(2); TreeNode *node3 = creatTreeNode(3); TreeNode *node4 = creatTreeNode(4); TreeNode *node5 = creatTreeNode(5); TreeNode *node6 = creatTreeNode(6); TreeNode *node7 = creatTreeNode(7); connectTree(node1, node2, node3); connectTree(node2, node4, node5); connectTree(node3, node6, node7); solve(node1); //DestroyTree(node1); return 0; } void dlr(TreeNode *root){ if(!root) return; stack <TreeNode* > sta; TreeNode *tmp = root; while(tmp != NULL || !sta.empty()){ while(tmp != NULL){ printf("%d ", tmp->value); sta.push(tmp); tmp = tmp->left; } if(!sta.empty()){ tmp = sta.top(); sta.pop(); tmp = tmp->right; } } puts(""); } void ldr(TreeNode *root){ if(!root) return; stack <TreeNode* > sta; TreeNode *tmp = root; while(tmp != NULL || !sta.empty()){ while(tmp != NULL){ sta.push(tmp); tmp = tmp->left; } if(!sta.empty()){ tmp = sta.top(); sta.pop(); printf("%d ", tmp->value); tmp = tmp->right; } } puts(""); } void lrd(TreeNode *root){ if(!root) return; stack <TreeNode* > sta; TreeNode *cur, *pre = NULL; sta.push(root); while(!sta.empty()){ cur = sta.top(); if( (cur->left == NULL && cur->right ==NULL) || (pre != NULL && (pre == cur->left || pre == cur->right))){ printf("%d ", cur->value); sta.pop(); pre = cur; }else{ if(cur->right != NULL) sta.push(cur->right); if(cur->left != NULL) sta.push(cur->left); } } puts(""); } void solve(TreeNode *root){ dlr(root); ldr(root); lrd(root); }
15.
查找链表中倒数第k个结点
解决方案:
设置两个指针,开始指向头结点,然后第一个移动并计数,当移动了k-1次另一指针开始移动,复杂度为o(n)
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <queue> using namespace std; struct Node{ int num; Node *next; }; Node *insert(Node *p, int num){ Node *s = new Node; s->num = num; s->next = NULL; p->next = s; return s; } int main(){ int num; Node *root = new Node; Node *p = root; int k; scanf("%d", &k); while(~scanf("%d", &num)){ p = insert(p, num); } int cnt = 0; Node *s1 = root; Node *s2 = root; while(s1 != NULL){ cnt ++; if(cnt >= k + 1){ s2 = s2->next; } s1 = s1->next; } printf("%d ", s2->num); return 0; }
16.
在排序数组中查找和为给定值的两个数字
解决方案:
确定第一个和最后一个的和,假如相加大于给定的数,则较大数前移,小于则较小数后移,相等就输出
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <queue> using namespace std; int a[100]; int main(){ int n; while(~scanf("%d", &n)){ for(int i = 0; i < n; i ++) scanf("%d", a + i); int num; scanf("%d", &num); int st = 0, ed = n - 1; while(true){ if(a[st] + a[ed] == num){ printf("%d %d ", a[st], a[ed]); break; }else if(a[st] + a[ed] > num) ed --; else st ++; } } return 0; }
17.
O(logn)求Fibonacci数列
解决方案:
(转)
下面介绍一种时间复杂度是O(logn)的方法。在介绍这种方法之前,先介绍一个数学公式:
{f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1
(注:{f(n+1), f(n), f(n), f(n-1)}表示一个矩阵。在矩阵中第一行第一列是f(n+1),第一行第二列是f(n),第二行第一列是f(n),第二行第二列是f(n-1)。)
有了这个公式,要求得f(n),我们只需要求得矩阵{1, 1, 1,0}的n-1次方,因为矩阵{1, 1, 1,0}的n-1次方的结果的第一行第一列就是f(n)。这个数学公式用数学归纳法不难证明。感兴趣的朋友不妨自己证明一下。
现在的问题转换为求矩阵{1, 1, 1, 0}的乘方。如果简单第从0开始循环,n次方将需要n次运算,并不比前面的方法要快。但我们可以考虑乘方的如下性质:
/ an/2*an/2 n为偶数时
an=
a(n-1)/2*a(n-1)/2 n为奇数时
要求得n次方,我们先求得n/2次方,再把n/2的结果平方一下。如果把求n次方的问题看成一个大问题,把求n/2看成一个较小的问题。这种把大问题分解成一个或多个小问题的思路我们称之为分治法。这样求n次方就只需要logn次运算了。
实现这种方式时,首先需要定义一个2×2的矩阵,并且定义好矩阵的乘法以及乘方运算。当这些运算定义好了之后,剩下的事情就变得非常简单。完整的实现代码如下所示。
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <queue> using namespace std; struct matrix2by2{ matrix2by2( long long m00 = 0, long long m01 = 0, long long m10 = 0, long long m11 = 0 ) :m_00(m00), m_01(m01), m_10(m10), m_11(m11){} long long m_00; long long m_01; long long m_10; long long m_11; }; matrix2by2 matrixmultiply(const matrix2by2 &a, const matrix2by2 &b){ return matrix2by2( a.m_00 * b.m_00 + a.m_01 * b.m_10, a.m_00 * b.m_01 + a.m_01 * b.m_11, a.m_10 * b.m_00 + a.m_11 * b.m_10, a.m_10 * b.m_01 + a.m_11 * b.m_11 ); } matrix2by2 matrixpower(int n){ matrix2by2 matrix; if(n == 1) matrix = matrix2by2(1, 1, 1, 0); else if(n % 2 == 0){ matrix = matrixpower(n/2); matrix = matrixmultiply(matrix, matrix); } else if(n % 2 == 1){ matrix = matrixpower((n-1)/2); matrix = matrixmultiply(matrix, matrix); matrix = matrixmultiply(matrix, matrix2by2(1, 1, 1, 0)); } return matrix; } int main(){ printf("%d ", matrixpower(10-1).m_00); return 0; }
18.
定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。
解决方案:
假设这字符串简化为XY,刚分三次操作即可,X旋转,Y旋转,XY旋转
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <queue> using namespace std; char s[] = "12345"; int n = 5; int k = 2; void reversestring(char *st, char *ed){ while(st < ed){ char tmp = *st; *st = *ed; *ed = tmp; st ++; ed --; } } int main(){ char *pst1 = s; char *ped1 = s + k - 1; char *pst2 = s + k; char *ped2 = s + n - 1; reversestring(pst1, ped1); reversestring(pst2, ped2); reversestring(pst1, ped2); printf("%s ", s); return 0; }
19.
解决方案:从右上角开始查找,如果大就消除该列,如果小就消除该行
(修改)
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <queue> using namespace std; bool find(int *a, int rows, int cols, int num){ int row = 0; int col = cols - 1; // printf("%d %d %d ", a[0], num, a[rows * cols - 1]); for(int i = 0; i < rows; i ++){ for(int j = 0; j < cols; j ++){ // printf("%d ", a[i * cols + j]); } // puts(""); } if(a[0] > num || a[rows * cols - 1] < num) return false; //puts("asdf"); while(row < rows && col >= 0){ if(a[row * cols + col] == num) return true; if(a[row * cols + col] > num) col --; else row ++; } return false; } void test(){ int a[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}}; bool ans = find((int*)a, 4, 4, 15); if( ans ) puts("yes"); else puts("no"); } int main(){ test(); return 0; }
20.
旋转数组二分查找
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <queue> using namespace std; int a[111]; void solve(int st, int ed, int num){ int index1 = st, index2 = ed, mid = st; while(index1 <= index2){ int mid = index1 + index2 >> 1; if(a[mid] == num){ puts("yes"); return; } if(a[mid] >= a[st]){ if(num < a[mid] && num >= a[index1]) index2 = mid - 1; else index1 = mid + 1; }else{ if(num > a[mid] && num <= a[index2]) index1 = mid + 1; else index2 = mid - 1; } } puts("no"); } int main(){ int n; scanf("%d", &n); for(int i = 0; i < n; i ++) scanf("%d", a + i); int num; scanf("%d", &num); solve(0, n - 1, num); return 0; }
21.
给一个数,打印从1到这个数位数的最大数字
#include <cstdio> #include <cstring> #include <stack> #include <set> #include <algorithm> #include <iostream> #include <queue> using namespace std; void PrintNum(char *num){ int len = strlen(num); bool f = 1; for(int i = 0; i < len; i ++){ if(f && num[i] == '0') continue; else f = 0; putchar(num[i]); } printf(" "); } bool Increment(char *num){ bool isOverflow = false; int nTakeover = 0; int len = strlen(num); for(int i = len - 1; i >= 0; i --){ int sum = num[i] - '0' + nTakeover; if(i == len - 1) sum ++; if(sum >= 10){ if(i == 0) isOverflow = true; else{ sum -= 10; nTakeover = 1; num[i] = '0' + sum; } }else{ num[i] = '0' + sum; break; } } return isOverflow; } int main(){ int n; scanf("%d", &n); char *num = new char [n + 1]; memset(num, '0', n); num[n] = '