最近在面试的过程中,遇到了很多手写代码的情况,我是真的不会写算法题,但是常见的还是要总结一下。
1.快速排序
这个我觉得背也要背下来。。。
用递归的思想,分别排序index左边的数组和index右边的数组。
⚠️:随机数的生成
#define random(a,b) (rand()%(b-a+1)+a) 生成[a,b]区间内的整数
int Partition(int data[],int length,int start,int end){
if (data == nullptr || length<=0||start<0||end>length) {
cout<<"error input"<<endl;
}
srand((unsigned)time(NULL));
int index = random(start,end);
swap(data[index], data[end]);
int small = start-1;
for (int i = start; i<end; i++) {
if (data[i]<data[end]) {
++small;
if (small != i) {
swap(data[small], data[i]);
}
}
}
++small;
swap(data[small], data[end]);
return small;
}
void QuickSort(int data[],int length,int start, int end){
if (start == end) {
return;
}
int index = Partition(data, length, start, end);
//对左边右边分别排序
if (index>start) {
QuickSort(data, length, start, index-1);
}
if (index<end) {
QuickSort(data, length, index+1, end);
}
}
2.求数组的全排列
首先我们明确全排列的概念,数组1,2,3的全排列就是123,132,213,231,312,321
也就是说就是把数组的后n-1个数全排列,然后再加上第一个数。
其实数组的全排列和字符串的全排列是一个思想:⚠️:如果不想用res然后打印res的话,也可以直接把num打印出来。
void permutationWithArray(vector> &res,vector&num,int index)
{
if (index>=num.size()) {
res.push_back(num);
return;
}
for (int i = index; i<num.size(); i++) {
swap(num[i], num[index]);
permutationWithArray(res,num,index+1);
swap(num[i], num[index]);
}
}
这里有一个相同思路的问题,就是字符串的全排列。一个字符串char a[] = "abc";那么a现在是abc,*a = a,如果a+1之后*a = b;
字符串的全排列:
void permutation(char* c,char* index){
if (*index == '\0') {
cout<<c<<endl;
}else{
for (char* pBegin = index; *pBegin!='\0'; pBegin++) {
swap(*pBegin, *index);//把字符串的第一个元素和每一个元素交换
permutation(c, index+1);
swap(*pBegin, *index);
}
}
}
3.调整一个数组中元素的顺序,让什么样的数位于什么样的数前面
可以用两个指针,一个指向数组的头部,一个指向数组的尾部,然后遍历数组。如果不满足条件就交换两个指针指向的元素,知道两个指针相等,顺序调换完毕。
4.反转链表
反转链表的时候需要注意,在遍历链表的时候要存储当前节点,当前节点的前一个节点,和当前节点的下一个节点。在反转链表的时候要注意,不能让链表断掉。
ListNode* reverseLinkList(ListNode* root){
//重点就是为了防止反转后的链表断裂,我们在遍历一个节点的时候要保留它自己,它的上一个节点和它的下一个节点。
ListNode* pNode = root;
ListNode* pReservedNode = nullptr;
ListNode* pPrev = nullptr;
while (pNode!=nullptr) {
ListNode* pNext = pNode->next;
if (pNext == nullptr) {
pReservedNode = pNode;
}
pNode->next = pPrev;
pPrev = pNode;
pNode = pNext;
}
return pReservedNode;
}
5.利用partition算法还能做什么
求数组中出现次数超过一半的数。
一个数在数组中出线次数超过一半,那么它一定是排好序的数组的中位数,也就是说我们找到数组的中位数就找到了这个数,也就是找这个数组中第n/2大的数字,我们有时间复杂度为O(n)的算法来求数组中第k大的数字,就是用Partition函数。当index是n/2的时候arr[index]就是第n/2大的数啦。
求数组中最小的k个数。
如果index的下标是k,那么arr[index]前面的k个数就是数组中最小的k个数,可以用这种方法在O(n)复杂度内求出数组中最小的k个数。
6.在排序数组中查找数字出现的次数
因为一个数组是排序的,那么如果一个数字重复出现了多次在数组中肯定是连着的,也就是说我们找到一个数字在数组中第一次出现的位置,再找到他第二次出现的位置,两次出现的位置相减加1,就是数字出现的次数。
⚠️:在找数字第一次出现的位置的时候,不能找到数字出现的位置就完了,还要看这个数字前面的数字是不是也是要找的数字,如果前面的数字不是要找的数字或者前面没有数字了说明就是第一次出现的位置,如果不是这样的话就要继续在前面的数组里查找。
7.查找有序数组中没有出现的数
一个有序数组的长度是n,数组中所有的数字都在0~n-1的范围内,只有一个数字没有出现在这个数组中,想要把这个数字找出来。
因为数组是一个有序数组,可以用二分查找,找到数组的中间位置上的数,如果这个数和自己的下标不相等,说明它或者它的前面的数就是少的那个,如果它前面的数和自己的下标相等,说明中间这个数是第一个值和下标不等的元素,他的下标就是缺失的数字。
中间位置上的数和自己的下标相等,说明少的那个数出现在数组的后半部分。3
8.设计一个包含min函数的栈
首先让我们设计的数据结构是栈,就必须满足栈先进后出的特点,无论是出栈还是入栈都在栈顶进行。其次要包含min函数,就是说要让栈有一个功能就是在O(1)的时间复杂度内弹出栈中的最小元素。
我们可以设计一个辅助栈,每次有元素入栈的时候都和当前栈中的最小元素比较,把较小的那一个压入辅助栈,如果最小值被弹出了,辅助栈的栈顶元素也会被弹出,弹出后辅助栈中的栈顶元素就是当前栈中的最小值。