大三菜鸟一枚,经历过几次笔试,全部挂了,甚至有些连简历都没过,继续努力,打好基础,早日拿到offer!!!
算法题(牛客网)
1.反转链表
空间复杂度O(1),意味着以下方案都不可行:
1.创建vector数组存储所有值,接着反转数组,再依次修改原本链表的值。
2.递归到最后一个节点,用一个新的链表进行反向进行链接。
所以能否使用几个常量来进行反转,很容易就想到用双指针(我学过哈哈哈)。用两个指针,分别指左边和右边,一步一步反转过来就可以。
详细代码:
非递归版本:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* left = nullptr,* right = pHead; //左指针指向nullptr,右指针指向头节点
while(pHead){
right = pHead->next; //右指针保存下一节点
pHead->next = left; //头指针指向左节点
left = pHead; //左指针更新为头指针
pHead = right; //头指针更新为右指针
}
return left; //左指针就是新的头指针
}
};
递归版本:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
return Reverse(pHead);
}
ListNode* Reverse(ListNode* right,ListNode* left = nullptr){ //创建两个指针分别指向左边和右边
if (!right){
return left; //当右边为空时返回左指针,因为左指针是新的头指针
}
ListNode* tem_right = right->next; //保存右边的下一节点
right->next = left; //右边的头指针指向左指针
return Reverse(tem_right,right); //将新的右指针和左指针传入
}
};
2.排序
要求时间复杂度O(n2),空间复杂度O(n),可以采用以下方法:
百度了一圈,好像没有空间复杂度O(n),时间复杂度O(n2)的哈哈哈,正常来说使用:冒泡、快排等。
我们肯定要目标更加远大,直接进阶要求:时间复杂度O(nlogn),就是归并排序!!!
归并排序就是典型的分治思想,将问题分成诺干个小问题,最后再合并在一起!
(这里使用一下他人的图牛客@数据结构和算法)
代码详情:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 将给定数组排序
* @param arr int整型vector 待排序的数组
* @return int整型vector
*/
vector<int> MySort(vector<int>& arr) {
// write code here
mergeSort(arr, 0, arr.size()-1); //传入分割函数,将数组分成一块块
return arr;
}
void mergeSort(vector<int>& arr,int l,int r){ //分割函数
if(l==r) return;
int mid = (l+r)/2;
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r); //对半分
merge(arr, l, mid, r); //合并函数
}
void merge(vector<int>& arr,int l,int mid, int r){ //合并函数
vector<int> help = vector<int>(r-l+1);
int i = 0;
int p1 = l,p2 = mid + 1;
for(;p1 <= mid && p2 <= r;++i){
help[i] = arr[p1] < arr[p2]?arr[p1++]:arr[p2++]; //将两个数组按顺序合起来
}
while(p1 <= mid){
help[i++] = arr[p1++]; //如果第一个数组还没遍历完则全部放入数组后面
}
while(p2 <= r){
help[i++] = arr[p2++]; //同理
}
for(i=0;i<r-l+1;++i){
arr[l+i] = help[i]; //将合并好的数组复制给arr
}
}
};
3.设计LRU缓存结构
由于要求操作复杂度都为O(1),整体采用哈希双链表指针(我网上学的哈哈哈)。这道题对刚学的我来说挺困难的,耗费了很多时间,不过了解了LRU算法的原理。
代码详情:
struct DListNode{ //自定义数据结构
int key,val; //保存key 和 value
DListNode* pre; //存储上一个指针
DListNode* next; //存储下一个指针
DListNode(int k,int v):key(k),val(v),pre(nullptr),next(nullptr){};
};
class Solution {
private:
int size = 0; //缓存结构内存大小
DListNode* head; //创建头指针
DListNode* tail; //创建尾指针
unordered_map<int, DListNode*> mp; //创建哈希表
public:
/**
* lru design
* @param operators int整型vector<vector<>> the ops
* @param k int整型 the k
* @return int整型vector
*/
vector<int> LRU(vector<vector<int> >& operators, int k) {
// write code here
if (k < 1 || operators.size() == 0) return {}; //当缓存结构为0或无缓存时返回null
this->size = k;
this->head = new DListNode(0,0);
this->tail = new DListNode(0,0);
this->head->next = this->tail;
this->tail->pre = this->head; //以上初始化和构建空双向链表
vector<int> res; //存储输出的答案
for(vector<int> op : operators){
if(op[0] == 1){ //执行存储操作
set(op[1],op[2]);
}
else if(op[0] == 2){ //执行读取操作
int value = get(op[1]);
res.push_back(value);
}
}
return res;
}
void set(int key,int val){ //存入缓存结构
if(!mp.count(key)){ //哈希表不存在当前存入数据,将数据存入缓存
DListNode* node = new DListNode(key,val);
mp[key] = node;
if(this->size <= 0){ // 如果缓存结构不足,则删除最近最少使用数据
removeLast();
}
else{
this->size--; //缓存结构充足,内存减一
}
insertFirst(node); //插入新数据
}
else{ //哈希表存在数据,则将数据更新到头部
mp[key]->val = val;
moveToHead(mp[key]);
}
}
int get(int key){ //获取数据
int ret = -1; //如果不存在,返回-1
if(mp.count(key)){ //哈希表存在,将数据更新直头部
ret = mp[key]->val;
moveToHead(mp[key]);
}
return ret;
}
void moveToHead(DListNode* node){
if(node->pre == this->head) return; //如果数据就在头部,则不需要更新
node->pre->next = node->next; //先移除该数据
node->next->pre = node->pre;
insertFirst(node); //重新插入数据
}
void removeLast(){ //删除最后一个数据
mp.erase(this->tail->pre->key); //将哈希表中的数据删除
DListNode* tem = this->tail->pre;
this->tail->pre->pre->next = this->tail;
this->tail->pre = this->tail->pre->pre;
delete tem;
}
void insertFirst(DListNode* node){ //将数据插入头部
node->pre = this->head;
node->next = this->head->next;
this->head->next->pre = node;
this->head->next = node;
}
};
4/5/6. 实现二叉树先序,中序和后序遍历
居然一题同时实现三种遍历太过分了!把它当作3道题不过分吧,而且我是递归和非递归两种版本都实现,这是基础,不用百度哈哈哈。
代码详情:
递归版本:
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
class Solution {
public:
/**
*
* @param root TreeNode类 the root of binary tree
* @return int整型vector<vector<>>
*/
vector<vector<int> > threeOrders(TreeNode* root) {
// write code here
vector<vector<int>> res; //存储最终答案
vector<int> ans; //存储每次遍历数据
preTraversal(root, ans); //先序遍历
res.push_back(ans); //存储先序遍历数据
ans = vector<int>(); //清空ans
midTraversal(root, ans); //同理
res.push_back(ans);
ans = vector<int>();
postTraversal(root, ans);
res.push_back(ans);
return res;
}
//先序遍历递归
void preTraversal(TreeNode* root,vector<int>& ans){
if (!root) return;
ans.push_back(root->val);
preTraversal(root->left, ans);
preTraversal(root->right, ans);
return;
}
//中序遍历递归
void midTraversal(TreeNode* root,vector<int>& ans){
if (!root) return;
midTraversal(root->left, ans);
ans.push_back(root->val);
midTraversal(root->right, ans);
return;
}
//后序遍历递归
void postTraversal(TreeNode* root,vector<int>& ans){
if (!root) return;
postTraversal(root->left, ans);
postTraversal(root->right, ans);
ans.push_back(root->val);
return;
}
};
非递归版本:
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct postTreeNode{ //后序遍历专用结构体
int flag = 0; //记录是否遍历左右节点,0表示未遍历,1表示遍历了左节点,2表示遍历了右节点
TreeNode* node;
postTreeNode(TreeNode* node):flag(0),node(node){};
};
class Solution {
public:
/**
*
* @param root TreeNode类 the root of binary tree
* @return int整型vector<vector<>>
*/
vector<vector<int>> threeOrders(TreeNode* root) {
// write code here
vector<vector<int>> res;
res.push_back(un_preTraversal(root)); //保存先序遍历数据
res.push_back(un_midTraversal(root));
res.push_back(un_postTraversal(root));
return res;
}
//非递归先序遍历,使用栈进行遍历
vector<int> un_preTraversal(TreeNode* root){
vector<int> ans; //存储数据
stack<TreeNode*> stack; //保存未遍历结束的节点
while(root || !stack.empty()){
if (root){
ans.push_back(root->val); //保存节点
stack.push(root->right);
root = root->left;
}
else{
root = stack.top();
stack.pop();
}
}
return ans;
}
//非递归中序遍历
vector<int> un_midTraversal(TreeNode* root){
vector<int> ans;
stack<TreeNode*> stack;
while(root || !stack.empty()){
if (root){
stack.push(root); //一开始不存储数据先保存节点
root = root->left;
}
else{
root = stack.top();
stack.pop();
ans.push_back(root->val); //第二次遍历到才存储数据
root = root->right;
}
}
return ans;
}
//非递归后序遍历
vector<int> un_postTraversal(TreeNode* root){
vector<int> ans;
stack<postTreeNode*> stack; //由于要记录根节点是否遍历了左右节点,要新建结构体
postTreeNode* node = new postTreeNode(root);//创建后序遍历专用节点
while(node->node || !stack.empty()){
if (node->node){
if (node->flag == 0){ //未遍历左右节点
node->flag = 1;
stack.push(node);
root = root->left;
node = new postTreeNode(root);
}
else if(node->flag == 1){ //未遍历右节点
node->flag = 2;
stack.push(node);
root = root->right;
node = new postTreeNode(root);
}
else if (node->flag == 2){ //两边都遍历,可以保存数据
ans.push_back(node->node->val);
node->node = nullptr; //不直接使用stack.top(),防止非法访问
}
}
else{
node = stack.top();
stack.pop();
root = node->node;
}
}
return ans;
}
};
7. 最小的K个数
看到要求空间复杂度O(n),时间复杂度O(nlogn),直接想到归并排序,排序完后输出前K个就能解决。但我还是觉得它想考我堆排。
代码详情:
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
if (input.size() == 0 || k <= 0) return {};
mergesort(input, 0, input.size()-1);
return vector<int>({input.begin(),input.begin()+k});
}
//分割函数
void mergesort(vector<int>& input,int l,int r){
if (l >= r){
return;
}
int mid = (l+r)/2;
mergesort(input,l,mid); //平均分割成左右两部分
mergesort(input, mid+1, r);
merge(input, l, mid,r); //合并数组
}
//合并函数
void merge(vector<int>& input,int l,int mid,int r){
vector<int> ans = vector<int>(r-l+1);
int i = 0; //记录新数据下标
int p1=l,p2=mid+1;
while(p1<=mid && p2<=r){
ans[i++] = input[p1] < input[p2]?input[p1++]:input[p2++];//保存小的数据
}
while(p1<=mid){ //判断左右哪个部分还有剩下的,保存剩下的数据
ans[i++] = input[p1++];
}
while(p2<=r){
ans[i++] = input[p2++];
}
i=0;
while(i<r-l+1){ //复制回原数组
input[l+i] = ans[i];
++i;
}
}
};
麻了麻了,没想到第一天不能实现每天10道笔试,10道面试,明天一定努力完成!!!