1.反转单链表,输入链表的头节点,输出该链表,并输出反转后的头节点
这个题目不用再说了,写过N边了
SLnode reverse(SLnode head) { SLnode reverse_head = NULL; SLnode pnode = head->next; SLnode prev = NULL; SLnode pnext = NULL; while(pnode != NULL) { pnext = pnode->next; if(pnext == NULL) reverse_head = pnode; pnode->next = prev; prev = pnode; pnode = pnext; } head->next = reverse_head; return head; }
以前自己还写过一段代码,两种方式的思路不同,这种是从第一个节点开始,把它的next指针指向它的前一个节点,直到最后一个节点为止
下一种也是从第一个节点开始,但是把它的下一个节点放到它的前面,比较不容易理解
SLnode reverse(SLnode head) { if(head == NULL || head->next == NULL) return head; SLnode p = NULL; SLnode tmp = head->next; while(tmp->next != NULL) { p = tmp->next; tmp->next = p->next; p->next = head->next; head->next = p; } return head; }
2.合并排序的链表,使合并后仍然递增,这个也是写了很多边了,递归和非递归实现
SLnode mergelist2(SLnode head1, SLnode head2) { if(head1 == NULL) return head2; if(head2 == NULL) return head1; SLnode MergerHead = NULL; if(head1->data > head2->data) { MergerHead = head1; MergerHead->next = mergelist2(head1->next, head2); } else { MergerHead = head2; MergerHead->next = mergelist2(head1, head2->next); } return MergerHead; }
再一次强调递归和非递归的本质区别,一个是使用系统提供的栈,即函数参数压栈,一个是我们自己使用栈,此处的非递归采用的是迭代,某种程度上来说,也是使用的栈
SLnode mergelist(SLnode head1, SLnode head2) { if(head1 == NULL) return head2; if(head2 == NULL) return head1; SLnode node1 = head1->next; SLnode node2 = head2->next; SLnode mergehead = (SLnode)malloc(sizeof(SLnode)); if(node1->data > node2->data) { mergehead->next = node1; node1 = node1->next; } else { mergehead->next = node2; node2 = node2->next; } SLnode temp = mergehead->next; while(node1 != NULL && node2 != NULL) { if(node1->data > node2->data) { temp->next = node1; node1 = node1->next; temp = temp->next; } else { temp->next = node2; node2 = node2->next; temp = temp->next; } } if(node1 != NULL) temp->next = node1; if(node2 != NULL) temp->next = node2; return mergehead; }
3.输入两颗二叉树,判断B是不是A的子结构
这个题目很有思想,我是看了思路之后才写出来的,需要分为两步考虑,首先从A中查找是否有B的根节点,如果找到了,再比较A的左子树和B的左子树,A的右子树和B的右子树是否相
等,如果找不到,则继续在A的左子树和右子树中查找是否有和B的根节点相同的,直到A的叶子节点为止
#include <stdio.h> #include <stdlib.h> typedef struct BitreeNode { int data; struct BitreeNode*left, *right; }BitreeNode; void construct_bitree(BitreeNode **root) { int x; scanf("%d",&x); if(x != -1) { *root = (BitreeNode*)malloc(sizeof(struct BitreeNode)); if(*root == NULL) printf("malloc error\n"); (*root)->data = x; (*root)->left = NULL; (*root)->right = NULL; printf("input the left node, -1 to end\n"); construct_bitree(&((*root)->left)); printf("input the right node, -1 to end\n"); construct_bitree(&((*root)->right)); } } void print_tree(BitreeNode *root) { if(root != NULL) { printf("%d\n",root->data); print_tree(root->left); print_tree(root->right); } } void Free(BitreeNode *root) { if(root != NULL) return; else { Free(root->left); Free(root->right); free(root); } } int find_subtree_core(BitreeNode *root1, BitreeNode *root2) { if(root1 == NULL) return 0; if(root2 == NULL) return 1; if(root1->data != root2->data) return 0; return find_subtree_core(root1->left, root2->right)&& find_subtree_core(root1->right, root2->right); } int find_subtree(BitreeNode *root1, BitreeNode *root2) { int ret = 0; if(root1 != NULL && root2 != NULL) { if(root1->data == root2->data) ret = find_subtree_core(root1, root2); else { if(!ret) find_subtree(root1->left, root2); if(!ret) find_subtree(root1->right, root2); } } return ret; } void main() { BitreeNode *root1 = NULL; BitreeNode *root2 = NULL; printf("please input the root1:\n"); construct_bitree(&root1); print_tree(root1); printf("please input the root2:\n"); construct_bitree(&root2); print_tree(root2); if(find_subtree(root1, root2)) printf("find the subtree\n"); Free(root1); Free(root2); }
4.二叉树的镜像,要求输入一个二叉树,输出它的镜像
思路:如果根节点的左右孩子节点存在,就交换根节点的左右孩子节点,如果左右孩子节点存在,再递归调用
void mirror_of_bitree(BitreeNode *root) { if(root == NULL || (root->left == NULL)&&(root->right == NULL)) return; BitreeNode *temp = root->left; root->left = root->right; root->right = temp; mirror_of_bitree(root->left); mirror_of_bitree(root->right); }
5.顺时针打印一个矩阵,如果输入的是
1 2 3 4
5 6 7 8
打印的是1 2 3 4 8 7 6 5
这个题目我按照自己思路来解决的,没有按照书上给的思路,直接来想就是打印最外面一圈,然后缩小范围,打印里面一圈,直到范围内曾大小为0时为止
#include <stdio.h> #define column 4 #define row 4 //这个程序是我自己写出来的,虽然比较臃肿和难以理解,但是思路很清晰 void main() { int a[row][column] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; int begin1 = 0, end1 = row-1; int begin2 = 0, end2 = column-1; int i = 0, j = 0; while(i <= end1) { if(j > end2) break; for( ; j <= end2; j++) printf("%d\n",a[i][j]); j--; i++; if(i > end1) break; for( ; i <= end1; i++) printf("%d\n",a[i][j]); j--; i--; if(j < begin2) break; for( ; j >= begin2; j--) printf("%d\n",a[i][j]); i--; j++; if(i <= begin1) break; for( ; i > begin1; i--) printf("%d\n",a[i][j]); i++; j++; begin1++;begin2++; end1--;end2--; } }
6.定一个栈的数据结构,并实现一个能够得到最小元素的min函数,调用min,pop,push函数的时间复杂度都是O(1)
首先我们要明白这个题目的入手点在于用栈去实现这样的数据结构,我当时还傻不啦叽的自己去实现一个栈,找到了切入点,我们再来看怎么去实现这个函数,
在栈中添加一个成员变量作为最小元素?那要是这个变量弹出了怎么办?所以,我们可以每次入栈元素的时候就添加一个变量,这个时候我们可以使用辅助栈,这里的关键在于
每次入栈的时候我们要去更新辅助栈中元素,使它的栈顶是最小元素
#include <iostream> #include <stack> #include <stdlib.h> using namespace std; template<class T> class Stack { private: stack<T> m_data; stack<T> m_min; public: Stack(); ~Stack(); void push(T data); void pop(); const T& min() const; }; template<class T> Stack<T>::Stack() {} template<class T> Stack<T>::~Stack() {} template<class T> void Stack<T>::push(T data) { m_data.push(data); if(!m_min.empty() && data > m_min.top()) m_min.push(m_min.top());//每次入栈都要考虑最小元素的入栈 else m_min.push(data); } template<class T> void Stack<T>::pop() { if(m_data.empty()) { cout<<"the stack is empty"<<endl; return; } m_data.pop(); m_min.pop(); } template<class T> const T& Stack<T>::min() const { return m_min.top(); } int main() { Stack<int> SS; SS.push(2); SS.push(3); SS.push(4); int ret = SS.min(); SS.pop(); cout<<"min num "<<ret<<endl; SS.push(1); SS.push(8); SS.pop(); SS.pop(); SS.push(7); ret = SS.min(); cout<<"min num "<<ret<<endl; return 0; }
7.一个整数数组里面,除了两个数字外,其它的数字都出现了两次,写出程序找出这两个只出现一次的数字,时间复杂度为O(n),空间复杂度为O(1)
这个题目还是曾经百度的笔试题目,思路是这样的:
既然其它数字都出现了两次,那么所有的数字求异或之后,得到的值就是只出现一次的那两个数字的异或值,然后我们找到这个值的二进制的最右边的那个1,并以此为依据,将这个数
组分为两部分,因为其它出现两次的数字在个各位上的值一样,所以这两部分就分出了这两个数字
一下是我的参考代码:
#include <stdio.h> int find_the_last1(int x) { int n = 0; x = x&~(x-1); while((1 & x) == 0) { x>>1; n++; } return n; } void num_appear_once(int a[], int n) { int ret = 0, ret1 = 0, ret2 = 0, i; for(i = 0; i < n; i++) ret ^= a[i]; int m = find_the_last1(ret); for(i = 0; i < n; i++) { if((a[i]>>m)&1) ret1 ^= a[i]; else ret2 ^= a[i]; } printf("%d\t%d\n",ret1, ret2); } void main() { int a[] = {1,2,3,3,2,5,6,1}; num_appear_once(a, 8); }
类比去年小米校招的题目:在一个长度为n的整形数组a里,除了三个数字只出现一次外,其他的数字都出现了2次。请写程序输出任意一个只出现一次的数字,程序时间和空间复杂度越小越好。例如: a = {1,3,7,9,5,9,4,3,6,1,7},输出4或5或6
这个题目的思路和上面是一样的,只不过是三个数字,所以在我们异或之后,我们可以两两异或,三个数的两两异或就可以分为两组,参考代码:
// lowbit表示的是某个数从右往左扫描第一次出现1的位置 int lowbit(int x) { return x&~(x-1); } void find(int* a , int n) { int i , xors; xors = 0; for(i = 0 ; i < n ; ++i) xors ^= a[i]; // 三个数两两的异或后lowbit有两个相同,一个不同,可以分为两组 int fips = 0; for(i = 0 ; i < n ; ++i) fips ^= lowbit(xors ^ a[i]);//这一步是个关键 // 表示的是:flips=lowbit(a^b)^lowbit(a^c)^lowbit(b^c) int b; // 假设三个只出现一次的其中一个数为b b = 0; for(i = 0 ; i < n ; ++i) { if(lowbit(xors ^ a[i]) == fips) b ^= a[i]; } // 成功找到三个数中一个数 cout<<b<<endl; }