补充的手写代码
C++实现构造函数和析构函数
class Test{
public:
Test(int a,int b):x(a),y(b){}
Test(const Test& ob){
x = ob.x;
y = ob.y;
}
Test& operator=(const Test& ob1){
x = ob1.x;
y = ob1.y;
return this;
}
~Test();
private:
int x,y;
};
用C++实现一个string(有时间再看)
//C++实现string
#pragma once
#include <cstdlib>
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
class MyString
{
public:
MyString(const char* pData = nullptr);//普通的构造函数
MyString(const MyString& otherStr);//拷贝构造函数
~MyString();
MyString& operator=(const MyString& otherStr);//赋值构造
MyString& operator=(const char* pData);
char& operator[](unsigned int index);
MyString operator+(const MyString &str);
MyString operator+(const char* pData);
bool operator==(const MyString &str);
bool operator==(const char* pData);
bool operator!=(const MyString &str);
bool operator!=(const char* pData);
friend ostream& operator<<(ostream& cout, const MyString& str);
friend istream& operator>>(istream& cin, MyString& str);
private:
char* m_pData;
int m_size;
int m_capacity;
};
ostream& operator<<(ostream& cout, const MyString& str);
istream & operator >> (istream & cin, MyString & str);
/* 普通构造函数 */
MyString::MyString(const char * pData)
{
if (nullptr == pData)
{
m_size = 0;
m_capacity = 100;
m_pData = new char[m_capacity]{ 0 };
return;
}
m_size = strlen(pData);
m_capacity = 2 * m_size + 1; //开辟2倍大空间,方便后续存储
m_pData = new char[m_capacity]{ 0 };
strcpy(m_pData, pData);
}
/* 拷贝构造函数 */
MyString::MyString(const MyString & otherStr)
{
if (nullptr == &otherStr) //参数传递引用时一定要做非空验证
{
m_size = 0;
m_capacity = 100;
m_pData = new char[m_capacity] { 0 };
return;
}
m_size = otherStr.m_size;
m_capacity = otherStr.m_capacity;
m_pData = new char[m_capacity]{ 0 };
strcpy(m_pData, otherStr.m_pData);
}
/* 析构函数 */
MyString::~MyString()
{
if(nullptr != m_pData)
delete[] m_pData;
}
MyString & MyString::operator=(const MyString & otherStr)
{
if (this != &otherStr) //剑指offer写法
{
MyString tmpStr(otherStr); //调用拷贝构造,交换字符串指针
char* p = tmpStr.m_pData;
tmpStr.m_pData = m_pData;
m_pData = p;
}
return *this;
}
MyString & MyString::operator=(const char * pData)
{
MyString tmpStr(pData); //调用构造函数
char* p = tmpStr.m_pData;
tmpStr.m_pData = m_pData;
m_pData = p;
return *this;
}
/* 返回新对象 */
MyString MyString::operator+(const MyString & str)
{
if (nullptr == &str)
{
return MyString(*this);
}
int newSize = m_size + str.m_size + 1;
char* temp = new char[newSize] {0};
strcat(temp, m_pData);
strcat(temp, str.m_pData);
MyString res(temp);
delete[] temp;
return res;
}
/* 返回新对象 */
MyString MyString::operator+(const char * pData)
{
if (nullptr == pData)
{
return MyString(pData);
}
int newSize = m_size + strlen(pData) + 1;
char* temp = new char[newSize] {0};
strcat(temp, m_pData);
strcat(temp, pData);
MyString res(temp);
delete[] temp;
return res;
}
char & MyString::operator[](unsigned int index)
{
return m_pData[index];
}
bool MyString::operator==(const MyString & str)
{
bool res = false;
if (nullptr == &str)
res = false;
if (this == &str)
res = true;
if (strcmp(m_pData, str.m_pData) == 0)
res = true;
return res;
}
bool MyString::operator==(const char * pData)
{
bool res = false;
if (nullptr == pData)
res = false;
if (strcmp(m_pData, pData) == 0)
res = true;
return res;
}
bool MyString::operator!=(const MyString & str)
{
return !operator==(str);
}
bool MyString::operator!=(const char * pData)
{
return !operator==(pData);
}
ostream& operator<<(ostream& cout, const MyString& str)
{
if (nullptr == &str)
return cout;
cout << str.m_pData;
return cout;
}
inline istream & operator >> (istream & cin, MyString & str)
{
if (nullptr == &str)
return cin;
memset(str.m_pData, 0, str.m_capacity); //清空数据
cin >> str.m_pData;
return cin;
}
int main()
{
//测试 构造函数 拷贝构造函数
MyString name = "Laymond";
MyString copyName = name;
cout << name << " " << copyName << endl;
//测试 [] 运算符
copyName[0] = 'A';
cout << copyName << endl;
//测试 + = 运算符
copyName = name + " is " + "good !";
cout << copyName << endl;
//测试<< >> == != 运算符
MyString str1, str2;
cin >> str1 >> str2;
cout << "str1:"<<str1 << endl;
cout << "str2:"<<str2 << endl;
cout << (MyString("hello") == "hello") << endl;
cout << (MyString("hello") != MyString("hello")) << endl;
cout << (MyString("hello") == MyString("hello")) << endl;
//在参数中引用的地方,一定要做非空验证,比如说如下,如果拷贝构造函数 没有做非空验证 程序会崩溃
MyString *pStr = NULL;
MyString str(*pStr);
return 0;
}
单例模式
class Singleton
{
public:
~Singleton(){
cout<<"destructor called!"<<endl;
}
//Singleton(const Singleton&)=delete;
//Singleton& operator=(const Singleton&)=delete;
static Singleton& get_instance(){
static Singleton instance;
return instance;
}
private:
Singleton(){
cout<<"constructor called!"<<endl;
}
};
int main(int argc, char *argv[])
{
Singleton& instance_1 = Singleton::get_instance();
Singleton& instance_2 = Singleton::get_instance();
return 0;
}
手写智能指针?????
在这里插入代码片
用C语言实现C++的继承和多态
typedef void (*FUN)();
struct A{
FUN fun();
int a;
};
struct B{
A _a;//继承
int b;
}
void fA(){
printf("A:fun()");
}
void fB(){
printf("B:fun()");
}
int main(){
A _a;
B _b;
a.fun() = fA;
b._a.fun() = fB;
A* p2 = &a;
p2->fun();
p2 = (A*)&b;
p2->fun();
return 0;
}
手写二分查找
int search(vector<int>& nums, int target) {
// write code here
int l=0,r=nums.size()-1;
int mid;
while(l<=r){
mid = l+(r-l)/2;//这个mid是放在这里的
if(target==nums[mid])
return mid;
}
else if(target>nums[mid])
r=mid-1;
else
l=mid+1;
}
return -1;
}
C语言指针版本
#include <iostream>
using namespace std;
//归并函数
void merge(int *R, int low, int mid,int high)
{
//需要定义一个额外的数组,临时存放归并的结果
int maxSize = high - low + 1;
int temp[7];//这里的大小是原数组的大小
for(int k=0;k<=high;k++)
temp[k]=R[k];
int i,j,k;
for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
if(temp[i]<=temp[j])
R[k]=temp[i++];
else
R[k]=temp[j++];
}
while(i<=mid)
R[k++]=temp[i++];
while(j<=high)
R[k++]=temp[j++];
}
void mergeSort(int *R, int low, int high)
{
if (low<high){
int mid = (low + high) / 2;
mergeSort(R, low, mid);
mergeSort(R, mid + 1, high);
merge(R, low, mid,high);
}
}
int main()
{
int R[] = {49, 36, 24, 65, 97, 6, 25};
int n = 7;
mergeSort(R, 0, n-1);
for (int i = 0; i < n; ++i)
cout << R[i] << " ";
cout<<endl;
return 0;
}
树的遍历完整代码:
思路:先用前序和中序建树,再遍历
最主要还是二叉树的结构体:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
合并K个链表(用堆)(不熟-1)堆要手写
class Solution {
struct cmp{//注意这里是怎么重载的
bool operator()(ListNode *a, ListNode* b){
return a->val > b->val;//C++默认大根堆,这里变成小根堆
}
};
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
// priority_queue<ListNode*,vector<ListNode*>,less<ListNode*>> q;
priority_queue<ListNode*, vector<ListNode*>,cmp> q;
for(int i=0;i<lists.size();i++){
if(lists[i])//注意这里要判断
q.push(lists[i]);
}
ListNode* head = new ListNode(-1);
ListNode* tem = head;
while(!q.empty()){
ListNode* f = q.top();
q.pop();
if(f->next)
q.push(f->next);
tem->next = f;
tem = tem->next;
}
return head->next;
}
};
暴力做法:
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
ListNode* ans = NULL;
for(int i=0;i<lists.size();i++){
ans = test(ans,lists[i]);
}
return ans;
}
ListNode* test(ListNode* p1,ListNode* p2){
if(!p1)
return p2;
if(!p2)
return p1;
ListNode* p = NULL;
if(p1->val<p2->val){
p=p1;
p->next = test(p1->next,p2);
}
else{
p=p2;
p->next = test(p1,p2->next);
}
return p;
}
};
14、剪绳子(不熟+1+1)
思路:
class Solution {
public:
int cuttingRope(int n) {
vector<int> dp(n+1,1);//这里
for(int i=1;i<=(n+1)/2;i++){//这里
for(int j=i;j<=n;j++){//这里
dp[j] = max(dp[j],dp[j-i]*i);//这里
}
}
return dp[n];
}
};
19、回文链表(这个方法不太好)
还有一种方法是用快慢指针,找到中间节点,前半部分和后半部分比较遍历
class Solution {
public:
bool isPalindrome(ListNode* head) {
//我想的方法是链表数据存入数组,然后再判断
if(head==NULL) return true;
vector<int> help;
while(head!=NULL)
{
help.push_back(head->val);
head=head->next;
}
for(int i=0;i<help.size()/2;i++)
if(help[i]!=help[help.size()-1-i]) return false;
return true;
}
};
22、二叉搜索树第k小节点
中序遍历,存数组
class Solution {
public:
TreeNode* KthNode(TreeNode* p,unsigned int k)
{
if(p==NULL||k<=0)
return NULL;
vector<TreeNode*> res;
test(p,res);
if(k>res.size())
return NULL;
return res[k-1];
}
void test(TreeNode* &p,vector<TreeNode*> &res){
if(p==NULL)
return;
test(p->left,res);
res.push_back(p);
test(p->right,res);
}
};
25、链表中倒数第k个节点以及删除操作(不熟+1)
快慢指针
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode* p1 = head;
ListNode* p2 = head;
while(k--&&p1){
//应该判断一下k是否大于链表的长
p1 = p1->next;
}
// p1 = p1->next;
while(p1){
p1 = p1->next;
p2 = p2->next;
}
return p2;
}
};
删除倒数第k个结点
ListNode* removeNthFromEnd(ListNode* head, int n) {
// write code here
if(head==NULL)
return NULL;
ListNode* pre = new ListNode(-1);
pre->next = head;
ListNode* first = pre;//注意
ListNode* slow = pre;
while(n--&&first){
first = first->next;
}
while(first->next){//注意
slow = slow->next;
first = first->next;
}
ListNode* tmp = slow->next;
slow->next = slow->next->next;
delete tmp;
return pre->next;
}
26、数值的整数次方(不熟)
快速乘法+位运算
class Solution {
public:
double myPow(double x, int n) {
long num = n;
if(n<0){
num = -num;
x = 1/x;
}
double res = 1;
while(num){//
if(num&1)//
res*=x;//
x*=x;//
num>>=1;//
}
return res;
}
};
27、构建乘积数组(不熟)
先左乘后右乘
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
vector<int> t(a.size(),1);
int l=1,r=1;
for(int i=0;i<t.size();i++){
t[i]*=l;
l*=a[i];
}
for(int i=t.size()-1;i>=0;i--){
t[i]*=r;
r*=a[i];
}
return t;
}
};
28、括号序列(不熟)
bool isValid(string s) {
// write code here
stack<char> c;
for(int i = 0 ; i < s.length(); i++)
{
if(c.empty())
{
c.push(s[i]);
continue;
}
if(s[i]==')'&&c.top()=='('){c.pop();}//注意if else
else if(s[i]=='}'&&c.top()=='{'){c.pop();}
else if(s[i]==']'&&c.top()=='['){c.pop();}
else{ c.push(s[i]);}
}
return c.empty();
}
29、大数相加(字符串)(不熟+1+1)
加法的进位永远不可能大于1
class Solution {
public:
string solve(string s, string t) {
if(s.empty())return t;
if(t.empty())return s;
if(s.size()<t.size())swap(t, s);//保证t的长度小
int tail=s.size()-t.size();
int carry=0,tmp=0;
while(tail--) t='0'+t;
for (int i=s.size()-1;i>=0;i--){
tmp=s[i]-'0'+t[i]-'0'+carry;
if(tmp>=10){
carry=1;//
tmp-=10;//
}
else{
carry=0;
}
s[i]=tmp+'0';
}
if (carry==1){
s='1'+s;
}
return s;
}
};
链表相加(不熟)
栈+反转链表
ListNode* addInList(ListNode* head1, ListNode* head2) {
// write code here
stack<int> st1;//存储链表1的数据
stack<int> st2;//存储链表2的数据
while(head1||head2)//head1和head2都遍历完时推出循环//注意
{
if(head1)
{
st1.push(head1->val);
head1=head1->next;
}
if(head2)
{
st2.push(head2->val);
head2=head2->next;
}
}
ListNode *vhead=new ListNode(-1);
int carry=0;
while(!st1.empty()||!st2.empty()||carry!=0)//这里设置carry!=0,是因为当st1,st2都遍历完时,如果carry=0,就不需要进入循环了
{
int a=0,b=0;
if(!st1.empty())
{
a=st1.top();
st1.pop();
}
if(!st2.empty())
{
b=st2.top();
st2.pop();
}
int get_sum=a+b+carry;//每次的和应该是对应位相加再加上进位
int ans=get_sum%10;//对累加的结果取余
carry=get_sum/10;//如果大于0,就进位
ListNode *cur=new ListNode(ans);//创建节点
cur->next=vhead->next;//这里心中右图就能做
vhead->next=cur;//每次把最新得到的节点更新到vhead->next中
}
return vhead->next;
}
30、大数相乘
substr(5)//表示从下标5开始在结尾
substr(5,8)//表示从下标5到8的数
class Solution {
public:
string multiply(string num1, string num2) {
int l1 = num1.size();
int l2 = num2.size();
string res(l1 + l2, '0');
for(int i=l1-1; i>=0; i--) {
for(int j=l2-1; j>=0; j--) {
int tmp = (res[i+j+1] - '0') + (num1[i] - '0')*(num2[j] - '0');
res[i+j+1] = tmp%10 + '0';
res[i+j] += tmp/10;
}
}
for(int i = 0; i < l1+l2; i++){
if(res[i]!='0') return res.substr(i);
}
return "0";
}
};
二叉搜索树的最近公共祖先节点
这是根据二叉搜索树的特性来的
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root->val<p->val&&root->val<q->val)
return lowestCommonAncestor(root->right,p,q);
if(root->val>p->val&&root->val>q->val)
return lowestCommonAncestor(root->left,p,q);
return root;
}
35、字符串反转(送分)
下面这个是原地交换的操作,或者1、开辟一个新数组2、直接用reverse函数
class Solution {
public:
string solve(string str) {
int len = str.length();
for(int i = 0 ; i < len/2 ;i++)
{
swap(str[i],str[len-1-i]);
}
return str;
}
};
36、求平方根(二分不熟)
int sqrt(int x) {
// write code here
long long i = 0;
while(i*i<x)
i++;
if(i*i==x)
return i;
return i-1;
}
二分法:
int sqrt(int x) {
// write code here
if(x<=1)
return x;
int l=1,r=x,mid=0;
while(l<=r){
mid = l+(r-l)/2;
if(mid==x/mid)//不要写成mid*mid==x,会内存溢出
return mid;
else if(mid>x/mid)
r = mid-1;
else
l = mid+1;
}
return r;//注意://结束条件end一定<begin,所以返回end
}
38、设计getMin功能的栈(不熟)
牛客网这里的第一个数是op,第二个才是值
stack<int> s,min_s;
vector<int> getMinStack(vector<vector<int> >& op) {
// write code here
vector<int> res;
for(int i=0;i<op.size();i++){
if(op[i][0]==1)
Push(op[i][1]);
else if(op[i][0]==2)
Pop();
else
res.push_back(get_min());
}
return res;
}
void Push(int x){
s.push(x);
if(min_s.empty()||x<min_s.top())//很奇怪,这里还有先后顺序的
min_s.push(x);
}
void Pop(){
if(!s.empty()){
if(s.top()==min_s.top())
min_s.pop();
s.pop();
}
}
int get_min(){
return min_s.top();
}
40、字符串排列
class Solution {
public:
vector<string> end;
void test(string str,int num)//str不能加&,不能传引用(+1)
{
int n=str.size();
if(num==n-1)
{
end.push_back(str);
}
for(int i=num;i<n;i++)
{
if(str[num]!=str[i] || num==i)
{
swap(str[num], str[i]);
test(str,num+1);
}
}
}
vector<string> Permutation(string str) {
test(str,0);
return end;
}
};
42、把数组排成最小的数
class Solution {
public:
string minNumber(vector<int>& nums) {
vector<string> res;
string t;
for(int i=0;i<nums.size();i++){
res.push_back(to_string(nums[i]));
}
sort(res.begin(),res.end(),[](string &s1,string &s2){return s1+s2<s2+s1;});
for(int i=0;i<res.size();i++){
t += res[i];
}
return t;
}
};
46、二叉树深度
下面是非递归做法,是层次遍历
int maxDepth(TreeNode* root) {
if(root == NULL) return 0; //根点为空返回0
queue<TreeNode*>q; //开一个队列用于存储遍历树的节点
int res = 0; //用来存最大高度
q.push(root);
int n = 0; //用来存储队列的大小
while(!q.empty()){ //队列不为空就进行如下操作
n = q.size();
for(int i = 0;i < n;i ++){ //将这一层节点的左右孩子全部入队列
TreeNode* node = q.front(); //取出队列的第一个元素
q.pop();
if(node->left) q.push(node->left); //左孩子如果不为空就进队列
if(node->right) q.push(node->right); //右孩子如果不为空就进队列
}
res ++; //每遍历一层res就++
}
return res;
}
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==NULL)
return 0;
int l = maxDepth(root->left);
int r = maxDepth(root->right);
return max(l,r)+1;
}
};
47、平衡二叉树(不熟)
求树高+二叉平衡树的判断
class Solution {
public:
int test(TreeNode* root){
if(root==NULL)
return 0;
int l = test(root->left);
int r = test(root->right);
return r>l?r+1:l+1;
}
bool isBalanced(TreeNode* root) {
if(root==NULL)
return true;
int l = test(root->left);
int r = test(root->right);
return abs(r-l)<=1&&isBalanced(root->left)&&isBalanced(root->right);
}
};
49、n的阶乘后面有几个0
class Solution {
public:
int trailingZeroes(int n) {
int t=0;
while(n){
t+=n/5;
n/=5;
}
return t;
}
};
面试进展
3-22字节面试中的算法题
合并多个有序数组
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//堆排序做法
struct Node
{
int val;
int col;
int row;
Node(int _val, int _row, int _col)
:val(_val)
,row(_row)
,col(_col)
{}
};
//向下调整
void Adjust(vector<Node*>& heap, int parent)
{
size_t child = parent * 2 + 1;
while (child < heap.size())
{
if (child + 1 < heap.size() && heap[child + 1]->val < heap[child]->val)
child++;
if (heap[child]->val < heap[parent]->val)
swap(heap[child], heap[parent]);
parent = child;
child = parent * 2 + 1;
}
}
void Heap_Sort(vector<Node*>& heap)
{
int parent = heap.size() / 2 - 1;
for (int i = parent; i >= 0; --i)
Adjust(heap, parent);
}
vector<int> MergeArrays(vector<vector<int> >& arrs)
{
vector<int> res;
int count = 0;
vector<Node*> heap;//这里也可以使用优先级队列或者set,不需要排序的过程;
for (size_t i = 0; i < arrs.size(); ++i)
{
heap.push_back(new Node(arrs[i][0],i,0));
count += arrs[i].size(); //用来确定最终的循环次数
}
//先将堆中元素排序
Heap_Sort(heap);
while (count--)
{
//将堆顶元素(最小值)插入到结果数组中
res.push_back(heap[0]->val);
//判断堆顶元素是不是其数组的最后一个元素
if (heap[0]->col == arrs[heap[0]->row].size() - 1)
{
heap[0]->val = INT_MAX;//将堆顶元素替换成最大值;
heap[0]->col = INT_MAX;
heap[0]->row = INT_MAX;
}
else
{
heap[0]->val = arrs[heap[0]->row][heap[0]->col + 1];
heap[0]->row = heap[0]->row;
heap[0]->col = heap[0]->col + 1;
}
Adjust(heap, 0);
}
return res;
}
//暴力做法
vector<int> test(vector<vector<int> >& arrs){
vector<int> t;
for(int i=0;i<arrs.size();i++){
for(int j=0;j<arrs[i].size();j++){//注意呀这里用 arrs[i].size()
if(arrs[i][j]!=0)
t.push_back(arrs[i][j]);
}
}
sort(t.begin(),t.end());//sort()时间复杂度是n*log2n
return t;
}
//归并排序
vector<int> Merge(vector<int> &a,vector<int> &b){
int m = 0,n = 0;
vector<int> res;
while(m<a.size()&&n<b.size()){
if(a[m]<b[n]){
res.push_back(a[m]);
m++;
}
else{
res.push_back(b[n]);
n++;
}
}
while(m<a.size()){
res.push_back(a[m]);
m++;
}
while(n<b.size()){
res.push_back(b[n]);
n++;
}
return res;
}
vector<int> MergeSort(vector<vector<int> > &arrs){
vector<int> t;
for(int i=0;i<arrs.size();i++){
t = Merge(t,arrs[i]);
}
return t;
}
int main()
{
vector<vector<int>> a{{1,2,7,9},{5,6},{3},{4,8}};//看看这里的vector初始化
vector<int> res1 = MergeArrays(a);//堆排序做法
vector<int> res2 = test(a);//暴力做法
vector<int> res3 = MergeSort(a);//归并排序
for (auto c : res3)
cout << c << " ";
cout << endl;
system("pause");
return 0;
}
三数之和
3-26阿里笔试
对于连续的只含有0和1的数组,拿掉一个数,问最大连续1的个数?
思想:对一维数组进行分段,按照连续1分段,存入二维数组中去,遍历二维数组,在两两行之间的最大和
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int test(vector<int>& nums){
vector<vector<int>> res;
int k=0;
for(int i=0;i<nums.size();i++){
vector<int> t;
while(nums[i]==1){
t.push_back(nums[i]);
i++;
}
res.push_back(t);
}
int t = 0;
for(int i=1;i<res.size();i++){
int len = res[i-1].size() + res[i].size();
t = max(t,len);
}
if(res.size()==1)
return nums.size()-1;
return t;
}
int main(){
int n,m,k;
vector<vector<int>> res;
cin>>n;
for(int i=0;i<n;i++){
vector<int> t;
cin>>m;
for(int j=0;j<m;j++){
cin>>k;
t.push_back(k);
}
res.push_back(t);
}
for(int i=0;i<res.size();i++){
cout<<test(res[i])<<endl;
}
return 0;
}
有关的图的最短路径问题
(不会)
4-4美团笔试
1、求集合的子序列
没有任何两个字符相同,空串也是子序列
例如:对于aabc,它的子序列有12个
“ ”、“a”、a、b、c、ab、ab、bc、ac、ac、abc、abc
解释:第一个a和第二个a的下标不同,空串也是子序列
2、球形蛋糕
切n刀,问分成几块
输入
2 表示切几刀
1 0 1表示竖着切,0为经度
0 180 0表示横着切,180表示纬度
3、
/**
* 小美发明了一个函数:f(x),表示将x的所有正整数因子提取出来之后从小到大排列,
再将数字拼接之后得到的数字串。例如:10的所有因子为1,2,5,10,
那么将这些因子从小到大排序之后再拼接得到的数字串为12510,即f(10)=12510。
*
小美十分讨厌数字k,如果f(x)中含有某个子串对应的数字等于k,
那么她的不高兴度就会增加1。例如:f(8)=1248,那么其所有的子串为:1,2,4,8,12,24,48,124,248,1248,
只要k等于其中任意一个值,那么小美的不高兴度就会增加1。
对于每一个数,其不高兴度至多增加1。
*
* 现在,有一个长度为n的正整数序列,定义其不高兴度为序列中所有数的不高兴度的总和。
小美想要知道自己对于这个序列的总不高兴度为多少。
*/
4、
/**
* @author zhangbingbing
* @version 1.0
* @date 2021/4/4 11:16
小美在路上看到一些小学生在玩跳方格,她也想跟着一起玩。
这个方格被划分为n×n的小方格,即有n行n列。
每一个小方格上面都是一个1~k的正整数。小美想依次从1,2,…,k这个顺序来跳。
一开始小美可以站在任意一个小方格。
从一个方格跳到另一个方格的花费为两个方格的曼哈顿距离。
小美想知道是否可以依照该顺序一直跳到k,如果可以,最小的总花费是多少。
两个格子(x1,y1),(x2,y2)的曼哈顿距离为:|x1-x2|+|y1-y2|。
例如(3,4),(1,6)的曼哈顿距离为|3-1|+|4-6|=4。
*/
5、前缀和
4-7快手面试
合并两个有序链表
(✅)
4-7华为笔试
1、分组游戏
输入n,表示有几个小朋友
接着输入n行,表示这些小朋友想和谁组队
demo1
6
a b
c d
e f
b c
d a
f e
输出:2
dome2
3
a b
b c
c a
输出;1
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <valarray>
#include <stack>
#include <map>
using namespace std;
int cmp(vector<string> a, vector<string> b) {
return a[0] < b[0];
}
int main()
{
int n;
vector<vector<string> > res;
// map<string, string> ma;
string s1, s2;
vector<string> ve;
cin>>n;
while(n--) {
cin >> s1 >> s2;
if(s1 > s2)
swap(s1, s2);
ve.push_back(s1);
ve.push_back(s2);
// ve2.push_back(s1);
res.push_back(ve);
ve.clear();
}
sort(res.begin(), res.end(), cmp);
int cnt = 0;
vector<string> tmp = res[0];//选定第一个区间
// cout << tmp[0] << " " << tmp[1] << endl;
for(int i = 1; i < res.size(); ++i) {
if(res[i][0] > tmp[1]) {//判断后面的区间的左边界,和tmp的右边界相比较(注意这里仅限于大于)
tmp = res[i];
cnt++;
}
else {
tmp[0] = min(tmp[0], res[i][0]);
tmp[1] = max(tmp[1], res[i][1]);
}
// cout << tmp[0] << " " << tmp[1] << endl;
}
cnt++;
cout << cnt << endl;
return 0;
}
2、计算任务的执行时间
dome1
输入:
1,3,4
0->2
输出:
8,3,7
解释,任务都是顺序执行的,但是0->2表示依赖关系,先执行完任务2,才能执行任务0
3、
一、数组
1、数组中超过一半的数字(×)
1、排序:时间复杂度O(nlog(n))
2、map :时间复杂度O(n),但是需要开辟额外空间O(n)
3、摩尔投票法:时间复杂度O(n)√
class Solution {
public:
int majorityElement(vector<int>& nums) {
//摩尔投票法,投我++,不投--,超过一半以上的人投我,那我稳赢哇
int count = 0, candidate = 0;
for(int n : nums)
{
if(count == 0) candidate = n;
if(n == candidate) count++;
else count--;
}
return candidate;
}
};
2、无序数组中未出现的最小正整数(×)
1、排序+查找,光是排序就要O(nlog(n))
2、下面这个方法的时间复杂度:O(n)
下面这个是用的计数排序
int minNumberdisappered(vector<int>& arr) {
// write code here
int n=arr.size();
for(int i=0;i<n;++i)
{
if(arr[i]>=1&&arr[i]<=n&&arr[i]!=arr[arr[i]-1])
swap(arr[i],arr[arr[i]-1]);
}
for(int i=0;i<n;++i)
{
if(arr[i]!=i+1)
return i+1;
}
return n+1;
}
3、圆圈中最后的剩下元素(×)
一个长度为n的数组,每m个删除一个,求最后的那个数
迭代法
时间复杂度:O(N)
空间复杂度: O(1)
class Solution {
public:
int LastRemaining_Solution(int n, int m) {
if(n<1||m<1)
return -1;
int t=0;
for(int i=2;i<=n;i++)
t = (t+m)%i;
return t;
}
};
4、扑克牌顺序(√)
class Solution {
public:
bool IsContinuous( vector<int> nums ) {
if(nums.size()==0)
return false;
int t1=0,t2=0;
sort(nums.begin(),nums.end());
int i;
for(i=0;i<nums.size();i++){
if(nums[i]==0)
t1++;
else
break;
}
for(int j=i+1;j<nums.size();j++){
if(nums[j]==nums[j-1])
return false;
else
t2+=nums[j]-nums[j-1]-1;
}
if(t1>=t2)
return true;
return false;
}
};
5、矩阵查找(×)
int i = 0, j = matrix[0].size() - 1;
while(i < matrix.size() && j >= 0){//注意这个边界条件
if (matrix[i][j] == target){
return true;
}
else if (matrix[i][j] < target){
i++;
}
else{
j--;
}
}
return false;
6、数组中最长连续子序列(×)
int MLS(vector<int>& arr) {
// write code here
// 1、排序+计数
sort(arr.begin(),arr.end());
int t = 1;
int maxt = 1;
for(int i=1;i<arr.size();i++){
if(arr[i]-1==arr[i-1])
t++;
else if(arr[i-1]!=arr[i])
t = 1;
maxt = max(maxt,t);
}
return maxt;
}
7、容器盛水问题(接雨水)(×)
下面这个是双指针
long long maxWater(vector<int>& height) {
// write code here
int left = 0, right = height.size() - 1;
long long ans = 0;//牛客网这里不加long不行
int left_max = 0, right_max = 0;
while (left < right) {
if (height[left] < height[right]) {
height[left] >= left_max ? (left_max = height[left]) : ans += (left_max - height[left]);
++left;
}
else {
height[right] >= right_max ? (right_max = height[right]) : ans += (right_max - height[right]);
--right;
}
}
return ans;
}
8、数组中连续子数组的最大和(✅)
重置数组,大于0的往后加
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int s = nums[0];//注意保存数组第一个的值
for(int i=1;i<nums.size();i++){
if(nums[i-1]>0)//记得要写上>0
nums[i]+=nums[i-1];
s = max(s,nums[i]);
}
return s;
}
};
9、顺时针打印矩阵(❌)
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
if(matrix.empty())//一定要加这个判断为空的条件❌❌❌注意判断空
return res;
int row1 = 0,row2 = matrix.size();
int col1 = 0,col2 = matrix[0].size();
vector<int> res;
while(true){
for(int i=col1;i<col2;i++){
res.push_back(matrix[row1][i]);
}
row1++;
if(row1>=row2)//注意等于号
break;
for(int i=row1;i<row2;i++){
res.push_back(matrix[i][col2-1]);
}
col2--;
if(col1>=col2)
break;
for(int i=col2-1;i>=col1;i--){
res.push_back(matrix[row2-1][i]);
}
row2--;
if(row1>=row2)
break;
for(int i=row2-1;i>=row1;i--){
res.push_back(matrix[i][col1]);
}
col1++;
if(col1>=col2)
break;
}
return res;
}
};
螺旋矩阵
vector<int> res;
if(matrix.empty())//一定要加这个判断为空的条件
return res;
int row1 = 0,row2 = matrix.size();
int col1 = 0,col2 = matrix[0].size();
while(true){
for(int i=col1;i<col2;i++){
res.push_back(matrix[row1][i]);
}
row1++;
if(row1>=row2)//注意等于号
break;
for(int i=row1;i<row2;i++){
res.push_back(matrix[i][col2-1]);
}
col2--;
if(col1>=col2)
break;
for(int i=col2-1;i>=col1;i--){
res.push_back(matrix[row2-1][i]);
}
row2--;
if(row1>=row2)
break;
for(int i=row2-1;i>=row1;i--){
res.push_back(matrix[i][col1]);
}
col1++;
if(col1>=col2)
break;
}
return res;
10、股票的最大利润(❌)
int maxProfit(vector<int>& prices) {
// write code here
if(prices.size()<2)
return 0;
int first = prices[0];
int maxP = prices[1] - first;
for(int i=1;i<prices.size();i++){
if(prices[i]<first)
first = prices[i];
if(prices[i]-first>maxP)
maxP = prices[i]-first;
}
return maxP;
}
❌11、输出最长递增子序列
❌12、两栈实现队列
✅13、输出最长不重复子串
✅14、合并两个有序数组(归并)
❌15、反转单词顺序(美团提前批)
class Solution {
public:
string reverseWords(string s) {
if(s.empty())
return s;
int l = 0;
string t = "";
for(int i=s.length()-1;i>=0;i--){
if(s[i]!=' ')
l++;
else if(s[i]==' '&&l!=0){
t+=s.substr(i+1,l) + ' ';
l=0;
continue;
}
}
if(l!=0)
t+=s.substr(0,l) + ' ';
if(t.size()>0)
t.erase(t.size()-1,1);//删除最后面的空格
return t;
}
};
二、链表
1、判断链表中没有环
快慢指针
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* first = head;
ListNode* slow = head;
while(first&&first->next){//注意
first = first->next->next;
slow = slow->next;
if(first==slow)
return true;
}
return false;
}
};
2、就地反转链表+递归做法
1、头插法(画图最快)2、借助数组3、递归
class Solution {
public:
ListNode* ReverseList(ListNode* head) {
ListNode* pre = NULL;
ListNode* p = head;
ListNode* post = head->next;
while(p){//注意这里的判断条件
p->next = pre;
pre = p;
p = post;
post = post->next;
}
return pre;
}
};
递归做法
ListNode* reverseList(ListNode* head) {
if (!head || !head->next) {
return head;
}
ListNode* newHead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}
3、两个链表的第一个公共节点
思路:看
A+B
B+A
最后面几个数必然一致
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* l1 = headA;
ListNode* l2 = headB;
while(l1!=l2){
l1=l1==NULL?headB:l1->next;
l2=l2==NULL?headA:l2->next;
}
return l1;
}
};
✅4、链表排序
归并排序:分段递归
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(!head||!head->next)
return head;
ListNode* pre;
ListNode* slow = head;
ListNode* first = head;
while(first&&first->next){//这个快慢指针看来都是这个判断条件
pre = slow;//注意这里
slow = slow->next;
first = first->next->next;
}
pre->next=NULL;//注意
return test(sortList(head),sortList(slow));//注意这里的参数
}
ListNode* test(ListNode* p1,ListNode* p2){
if(!p1)//注意
return p2;
if(!p2)
return p1;
if(p1->val<p2->val){
p1->next = test(p1->next,p2);
return p1;//注意
}
else{
p2->next = test(p2->next,p1);
return p2;
}
}
};
✅5、判断链表环的入口节点
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* p) {
ListNode* fast = p;
ListNode* slow = p;
while(fast&&fast->next){
fast = fast->next->next;
slow = slow->next;
if(fast==slow)//注意这里
break;
}
if(!fast||!fast->next)
return NULL;
fast = p;
while(fast!=slow){//注意这里的判断条件
fast=fast->next;
slow = slow->next;
}
return fast;
}
};
❌6、链表中的节点每k个一组翻转
循环+递归+就地排序
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
// write code here
if(!head||!head->next||k<=1)
return head;
ListNode* p = head;
int len = 0;
while(p){
len++;
p = p->next;
}
if(len<k)
return head;
p = head;
ListNode* pre = new ListNode(0);
pre->next = NULL;
ListNode* post = head->next;
for(int i=0;i<k;i++){
p->next = pre->next;
pre->next = p;
p = post;
if(post)//注意
post = post->next;
}
ListNode* tail = pre->next;
while(tail->next)//注意(+1)
tail = tail->next;
tail->next = reverseKGroup(p,k);//注意
return pre->next;
}
};
❌7、合并两个排序链表(非递归不熟-1)
借用额外的两个指针
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// write code here
if(l1==NULL)
return l2;
if(l2==NULL)
return l1;
ListNode* head = (l1->val<=l2->val)?l1:l2;
ListNode* tail = head;
l1 = (head==l1)?l1->next:l1;
l2 = (head==l2)?l2->next:l2;
while(l1&&l2){
if(l1->val<=l2->val){
tail->next = l1;
l1=l1->next;
}
else{
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
tail->next = (l1==NULL)?l2:l1;//注意这里
return head;
}
不知道为啥我会写成下面这种,不是if嘛,脑子坏了
while(l1)
tail->next = l1;
while(l2)
tail->next = l2;
下面这个做法是递归的,最后不用
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* p1 = NULL;
if(l1==NULL)
return l2;
if(l2==NULL)
return l1;
if(l1->val<l2->val){
p1=l1;
p1->next=mergeTwoLists(l1->next,l2);
}
else{
p1=l2;
p1->next=mergeTwoLists(l1,l2->next);
}
return p1;
}
};
❌8、删除排序链表的重复节点
class Solution {
public:
ListNode* deleteDuplication(ListNode* pH) {
ListNode* preH = new ListNode(-1);
preH->next = pH;
ListNode* pre = preH;
ListNode* cur = pH;
while(cur){//注意
if(cur->next&&cur->val==cur->next->val){
cur=cur->next;//删除第一个重复节点
while(cur->next&&cur->val==cur->next->val)
cur=cur->next;
cur=cur->next;//删除最后一个重复节点
pre->next = cur;//注意这一步
}
else{
pre = cur;
cur = cur->next;
}
}
return preH->next;
}
};
三、树
1、对称二叉树(×)
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root==NULL)
return true;
return test(root->left,root->right);
}
bool test(TreeNode* l1,TreeNode* l2){
if(l1==NULL&&l2==NULL)
return true;
if(l1==NULL||l2==NULL)
return false;
if(l1->val!=l2->val)
return false;
return test(l1->left,l2->right)&&test(l1->right,l2->left);
}
};
2、根节点到叶子节点的和为target的路径(√)
class Solution {
public:
//先序遍历
vector<vector<int>> pathSum(TreeNode* root, int target) {
vector<vector<int>> res;
vector<int> t;
if(root==NULL)
return res;
test(root,res,t,0,target);
return res;
}
void test(TreeNode* &root,vector<vector<int>> &res,vector<int> &t,int cout,int Tsum){
cout+=root->val;
t.push_back(root->val);
bool temp = (root->left==NULL&&root->right==NULL);
if(temp&&cout==Tsum)
res.push_back(t);
if(root->left)
test(root->left,res,t,cout,Tsum);
if(root->right)
test(root->right,res,t,cout,Tsum);
t.pop_back();
}
};
3、二叉树镜像(×)
我就用递归做拉倒吧
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(root==NULL)
return NULL;
swap(root->left,root->right);
mirrorTree(root->left);
mirrorTree(root->right);
return root;
}
};
✅4、二叉树的先中后序非递归遍历
先序递归
class Solution {
public:
vector<int> res;
vector<int> preorderTraversal(TreeNode* root) {
if(root){
res.push_back(root->val);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
return res;
}
};
先序非递归都要借助栈来实现
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(root==NULL)
return res;
stack<TreeNode*> s;
TreeNode* p = root;
while(!s.empty()||p){//注意
while(p){//注意
res.push_back(p->val);
s.push(p);
p = p->left;
}
p = s.top();
s.pop();
p = p->right;
}
return res;
}
};
中序
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(root==NULL)
return res;
stack<TreeNode*> s;
TreeNode* p = root;
while(!s.empty()||p){
while(p){
s.push(p);
p = p->left;
}
p = s.top();
res.push_back(p->val);
s.pop();
p = p->right;
}
return res;
}
};
后序
class Solution {
public:
vector<int> postorderTraversal(TreeNode *root) {
vector<int> res;
if (root == NULL) {
return res;
}
stack<TreeNode *> s;
TreeNode *prev = NULL;
while (root != NULL || !s.empty()) {
while (root != nullptr) {
s.push(root);
root = root->left;
}
root = s.top();
s.pop();
if (root->right == NULL || root->right == prev) {
res.push_back(root->val);
prev = root;
root = NULL;
} else {
s.push(root);
root = root->right;
}
}
return res;
}
};
5、重建二叉树(×)
class Solution {
public:
int findA(vector<int> t,int val){
for(int i=0;i<t.size();i++){
if(t[i]==val)
return i;
}
return -1;
}
void test(TreeNode* &root,vector<int>& preorder, vector<int>& inorder,int lp,int rp,int li,int ri){
if(lp>rp||li>ri)
return ;
root = new TreeNode(preorder[lp]);
int pos = findA(inorder,preorder[lp]);
test(root->left,preorder,inorder,lp+1,lp+pos-li,li,pos-1);
test(root->right,preorder,inorder,lp+pos-li+1,rp,pos+1,ri);
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size()<0)
return NULL;
TreeNode* root = NULL;
test(root,preorder,inorder,0,preorder.size()-1,0,inorder.size()-1);
return root;
}
};
✅6、二叉树的层次遍历即变形(√)
class Solution {
public:
vector<vector<int> > levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root==NULL)//这个判断条件必须要加
return res;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int n = q.size();
vector<int> t;
for(int i=0;i<n;i++){
TreeNode* p = q.front();
t.push_back(p->val);
q.pop();
if(p->left)
q.push(p->left);
if(p->right)
q.push(p->right);
}
res.push_back(t);
}
return res;
}
};
之字形打印
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root==NULL)
return res;
queue<TreeNode*> q;
q.push(root);//注意细节,不要连入栈都忘记写了
int count = 0;
while(!empty(q)){
int n = q.size();
vector<int> t;
for(int i=0;i<n;i++){
TreeNode* l = q.front();
t.push_back(l->val);
q.pop();
if(l->left)
q.push(l->left);
if(l->right)
q.push(l->right);
}
if(count%2==1)
reverse(t.begin(),t.end());
res.push_back(t);
count++;
}
return res;
}
};
✅7、二叉树的最近公共祖先节点(不熟)
LeetCode:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==p||root==q||!root)
return root;
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right = lowestCommonAncestor(root->right,p,q);
if(!left)
return right;
if(!right)
return left;
return root;
}
};
牛客网屁事多
int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
// write code here
return CommonAncestor(root, o1, o2)->val;
}
TreeNode* CommonAncestor (TreeNode *root, int o1, int o2) {
if (root == NULL || root->val == o1 || root->val == o2) { // 超过叶子节点,或者root为p、q中的一个直接返回
return root;
}
TreeNode* left = CommonAncestor(root->left,o1,o2); // 返回左侧的p\q节点
TreeNode* right = CommonAncestor(root->right,o1,o2); // 返回右侧的p\q节点
if (left == NULL) { // 都在右侧
return right;
}
if (right == NULL) { // 都在左侧
return left;
}
return root; // 在左右两侧
}
❌8、树的子结构
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(A==NULL||B==NULL)
return false;
bool res = false;
if(A->val==B->val)
res = test(A,B);
if(!res)
res = isSubStructure(A->left,B);
if(!res)
res = isSubStructure(A->right,B);
return res;
}
bool test(TreeNode* l1,TreeNode* l2){
if(l2==NULL)
return true;
if(l1==NULL)
return false;
if(l1->val!=l2->val)
return false;
return test(l1->left,l2->left)&&test(l1->right,l2->right);
}
};
四、排序
数组排序
#include<iostream>
#include<bits/stdc++.h>//priority_queue引用的库 ,也是sort引用的库
#include<vector>
using namespace std;
//快速排序---------------------------------------------
int once_quick_sort(vector<int>& t,int low,int high){
int key = t[low];
while(low<high){
// while(low<high&&key<=t[high--]),这样写出错 ,要写成下面那样
while(low<high&&key<=t[high])
high--;
if(low<high)
t[low++] = t[high];
while(low<high&&key>=t[low])
low++;
if(low<high)
t[high--] = t[low];
}
t[low] = key;
return low;
}
void quick_sort(vector<int>& t,int low,int high){
if(low>=high)
return ;
int mid = 0;
mid = once_quick_sort(t,low,high);
quick_sort(t,low,mid-1);
quick_sort(t,mid+1,high);
}
//归并函数---------------------------------------------
void merge(vector<int> &arr, int left, int mid, int right) {
vector<int> tmp(right-left+1); // 开辟新的数组
int i = left, j = mid+1, k = 0; // i左边数组的起始位置,j右边数组的起始位置,k已经放入新数组的个数
while (i <= mid && j <= right) { // 比较左右数组,数值小的放入新数组中
tmp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
}
while (i <= mid) tmp[k++] = arr[i++]; // 如果左半边数组没有排序完,加入新数组
while (j <= right) tmp[k++] = arr[j++]; // 如果右半边数组没有排序完,加入新数组
for (i = left, k = 0; i <= right;) arr[i++] = tmp[k++]; // 新数组数据放回到原来的数组中
}
void mergeSort(vector<int> &arr, int left, int right) {
if (left >= right) return;
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid); // 递归 左半边
mergeSort(arr, mid+1, right); // 递归 右半边
merge(arr, left, mid, right); // 合并两个有序的数组(参考合并两个有序的链表)
}
//堆排序-----------------------------------------
void heapSort(vector<int> &arr) {
priority_queue<int,vector<int>,greater<int>> q; //小顶堆
for(int i = 0; i < arr.size(); i++) {
q.push(arr[i]);
}
for (int i = 0; i < arr.size(); i++) {
arr[i] = q.top();
q.pop();
}
}
//上面的是调用了库函数
void shiftDown(vector<int> &t, int k, int len) {
int i=k,j=i*2;
while(j<=len)
{
if(j+1<=len&&t[j+1]>t[j])
{
j = j+1;
}
if(t[j]>t[i])
{
swap(t[i],t[j]);
i=j;
j=i*2;
}
else
break;
}
}
void Build_Max_Heap(vector<int> &t, int len) {
for(int i=len/2; i>=0; --i) {
shiftDown(t, i, len);
}
}
void hand_heapSort(vector<int>& t){
Build_Max_Heap(t,t.size()-1);//初始堆
for(int i=t.size()-1; i>=1; --i) {
swap(t[i],t[0]); //把堆顶元素和堆低元素交换
shiftDown(t, 0, i-1);//调整
}
}
int main()
{
cout<<"此排序demo不仅本地能通过,还得要能通过牛客网的测试"<<endl;
cout<<endl;
vector<int> res{49, 36, 24, 65, 97, 6, 25};
vector<int> res2{49, 36, 24, 65, 97, 6, 25};
vector<int> res3{49, 36, 24, 65, 97, 6, 25};
cout<<"归并排序:";
mergeSort(res, 0, res.size()-1);
for(int i=0;i<res.size();i++)
cout<<res[i]<<" ";
cout<<endl;
cout<<"快速排序:";
quick_sort(res2, 0, res2.size()-1);
for(int i=0;i<res2.size();i++)
cout<<res2[i]<<" ";
cout<<endl;
cout<<"堆排序:";
hand_heapSort(res3);
// heapSort(res3);
for(int i=0;i<res3.size();i++)
cout<<res3[i]<<" ";
cout<<endl;
return 0;
}
✅快速排序
✅归并排序
✅堆排序
1、奇数移动到偶数前面,且保持相对顺序不变(×)
思路:这题要求相对位置保持不变不就是在要求算法稳定性吗
快速排序是不稳定算法,若不要求保持相对顺序的话,可以用吉大的快排
吉大快排:
int i=0,j=nums.size()-1;
while(i<=j){
while(i<=j&&nums[i]%2==1)
i++;
while(i<=j&&nums[j]%2==0)
j--;
if(i<=j){
swap(nums[i],nums[j]);
i++;
j--;
}
}
return nums;
class Solution {
public:
vector<int> reOrderArray(vector<int>& array) {
// write code here
/**
1.冒泡排序,空间复杂度为O(1)
2.空间复杂度O(n)
3.相对位置可以改变可以用快速排序
*/
int flag = true;
while (flag) {
flag = false;
for (int i = 1; i < array.size(); ++i) {
if(array[i-1]%2==0 && array[i]%2){
flag = true;
swap(array[i-1],array[i]);
}
}
}
return array;
}
};
堆排序:
1、出现次数的topK问题(❌)
#include<unordered_map>
class Compare
{
public:
bool operator()(const pair<int, string>& a, const pair<int, string>& b) const
{
if(a.first == b.first) return a.second<b.second;
return a.first > b.first;
}
};
class Solution {
public:
/**
* return topK string
* @param strings string字符串vector strings
* @param k int整型 the k
* @return string字符串vector<vector<>>
*/
vector<vector<string> > topKstrings(vector<string>& strings, int k) {
//if(strings.empty() || k<=0) return {};
unordered_map<string, int> mp;
for(auto str : strings) ++mp[str];
priority_queue< pair<int, string>, vector<pair<int, string>>, Compare > q;
auto it = mp.begin();
for(int i=0; i<k&&it!=mp.end(); ++i)
{
q.o(it->second, it->first);
++it;
}
while(it!=mp.end())
{
if(it->second > q.top().first || (it->second==q.top().first && it->first<q.top().second))
{
q.pop();
q.emplace(it->second, it->first);
}
++it;
}
vector<vector<string>> ret;
while(!q.empty())
{
ret.push_back({q.top().second, to_string(q.top().first)});
q.pop();
}
reverse(ret.begin(), ret.end());
return ret;
}
};
2、数组中的前k个数(✅)
堆排序出入都是从根执行的
C++默认是大根堆
//升序队列,小顶堆
2 priority_queue <int,vector<int>,greater<int> > q;
3 //降序队列,大顶堆
4 priority_queue <int,vector<int>,less<int> >q;
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> t;
if(input.size()<k)
return t;
priority_queue<int ,vector<int>,greater<int>> q;//小根堆
for(auto c:input)
q.push(c);
while(k--){
t.push_back(q.top());
q.pop();
}
return t;
}
3、数组中找第K大的数(❌)要用小根堆?
这题二分查找暂时还不会
class Solution {
public:
int findKth(vector<int> a, int n, int k) {
// write code here
priority_queue<int, vector<int>, greater<int>> q;
for(auto c:a){
q.push(c);//这里是先进堆后出堆
if(q.size()>k)
q.pop();
}
return q.top();
}
};
五、二分
1、有重复数字的升序数组的二分查找(×)
对于:1 2 2 3 4,target = 2,返回的下标是1
下面这个是要返回第一个重复数字的下标
结合下面第三题的改良版
:
就是找下界
int search(vector<int>& nums, int target) {
// write code here
int l=0,r=nums.size();
while(l<r){
int mid = l+(r-l)/2;
if(nums[mid]<target)
l = mid+1;
else
r = mid;
}
return (l<nums.size() && nums[l]==target)?l:-1;
}
2、排序数组中的缺失数字(×)
int missingNumber(vector<int>& nums) {
int i = 0;
int j = nums.size() - 1;
while(i <= j){//这里必须要等于
int mid = (i + j)/2;
if(nums[mid] == mid){ // mid之前的元素位置正确
i = mid + 1; //查找后面的元素情况
}else{ //mid之后的元素位置正确
j = mid - 1; //查找前面的元素情况
}
}
return i;
}
3、数字在升序数组中出现的次数(×)
1、暴力遍历
时间复杂度:O(N)
空间复杂度:O(1)
2、二分
时间复杂度:O(logN)
空间复杂度:O(1)
先查找目标范围的下界和上界。
下界定义为:如果存在目标值,则指向第一个目标值,否则,如果不存在, 则指向大于目标值的第一个值。
上界定义为:不管目标值存在与否,都指向大于目标值的第一个值。
class Solution {
public:
int GetNumberOfK(vector<int> nums ,int target) {
int lbound = 0, rbound = 0;
// 寻找上界
int l = 0, r = nums.size();
while (l < r) {
int mid = l + (r - l) / 2;
if (nums[mid] < target) {
l = mid + 1;
}
else {
r = mid;
}
}
lbound = l;
// 寻找下界
l = 0, r = nums.size();
while (l < r) {
int mid = l + (r - l) / 2;
if (nums[mid] <= target) {
l = mid + 1;
}
else {
r = mid;
}
}
rbound = l;
return rbound - lbound;
}
};
4、旋转有序数组的最小整数(×)
二分
时间复杂度:所以为O(longN), 但是如果是[1, 1, 1, 1],会退化到O(n)
空间复杂度:没有开辟额外空间,为O(1)
class Solution {
public:
int minNumberInRotateArray(vector<int> nums) {
if(nums.size()==0)
return 0;
int l=0,r=nums.size()-1;
while(l<r){
if(nums[l]<nums[r])
return nums[l];
int mid = l + (r-l)/2;
if(nums[mid]>nums[r])
l = mid+1;
else if(nums[mid]<nums[r])
r = mid;
else
r--;
}
return nums[l];
}
};
5、有转动过的有序数组中寻找目标值(❌)
int search(int* A, int n, int target) {
// write code here
int left = 0, right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (A[mid] == target) return mid;
if (A[mid] >= A[left]) {
// 左侧有序(含A[mid])
if (A[mid] > target && A[left] <= target)//注意这里要等于
right = mid - 1;
else
left = mid + 1;
} else {
// 右侧有序(含A[mid])
if (A[mid] < target && A[right] >= target)//注意这里要等于
left = mid + 1;
else
right = mid - 1;
}
}
return -1;
}
六、dp
1、丑数(×)
思想:三行代表三个数组存放2,3,5的倍数,遍历三个数组找最小
1*2 2*2 3*2
1*3 2*3 3*3
1*5 2*5 3*5
int GetUglyNumber_Solution(int n) {
vector<int> dp(n+1,0);
dp[0]=1;
int p2=0,p3=0,p5=0;
for(int i=1;i<=n;i++){//注意这里
dp[i] = min(min(dp[p2]*2,dp[p3]*3),dp[p5]*5);
if(dp[i]==dp[p2]*2)
p2++;
if(dp[i]==dp[p3]*3)
p3++;
if(dp[i]==dp[p5]*5)
p5++;
}
return dp[n-1];//1是第一个丑数,所以返回n-1
}
2、最长回文子串(×)
class Solution {
public:
int getLongestPalindrome(string A, int n) {
if(n <= 1) return n;
int longest = 1;
bool dp[n][n];
for(int i = 0; i < n; i++) { //从短到长对每种长度分别判断,可以这么做是因为判断较长的需要利用较短的
for(int j = 0; j< n - i; j++) { //从头开始对长度i+1的子字符串判断
if(i == 0) dp[j][j] = 1; //长度1一定为回文
else if(i == 1) dp[j][j + 1] = (A[j] == A[j + 1]); //长度2判断头尾是否相等
else if(A[j] == A[j + i]) dp[j][j + i] = dp[j + 1][j + i - 1]; //长度大于等于3,判断两头是否相等,若相等则同去两头的bool值一样
else dp[j][j + i] = 0; //否则为0
if(dp[j][j + i]) longest = max(longest, i + 1); //更新最大值
}
}
return longest;
}
};
3、最长公共子串(×)
动态规划
string LCS(string str1, string str2) {
// write code here
int m = str1.size();
int n = str2.size();
// dp[i][j] str1前i个字符和str2前j个字符(以其为尾字符)的最长公共子串长度
int dp[m+1][n+1];
int maxlen = 0, end = 0;
//base case
for(int i = 0; i <= m; ++i) dp[i][0] = 0;
for(int j = 0; j <= n; ++j) dp[0][j] = 0;
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) {
if(str1[i-1] == str2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;//注意这里(+1)
else dp[i][j] = 0;
if(dp[i][j] > maxlen) {//注意这个if
maxlen = dp[i][j];
end = j - 1;
}
}
}
string r;
if(maxlen == 0) return "-1";
else r = str2.substr(end-maxlen+1, maxlen);
return r;
}
4、青蛙跳台阶(✅)
class Solution {
public:
int numWays(int n) {
vector<int> dp(n+1,1);
for(int i=2;i<=n;i++){
dp[i] = (dp[i-1] + dp[i-2])%1000000007;
}
return dp[n];
}
};
七、hashmap
1、两数之和(✅)
map+target-nums[i]
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// write code here
unordered_map<int,int> m;
for(int i=0;i<nums.size();i++){
auto it = m.find(target-nums[i]);
if(it!=m.end())
return {it->second+1,i+1};//返回的数据顺序会有影响
m[nums[i]] = i;//别忘记写了
}
return {};
}
};
2、三数之和(✅)
完整代码
//三数之和
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
vector<vector<int>> test1(vector<int> &nums){
//双指针做法,时间复杂度O(n^2)
vector<vector<int>> res;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++){
if(nums[0]>0)
break;
if(i>0&&nums[i-1]==nums[i])
continue;
int l=i+1,r=nums.size();
while(l<r){
int s = nums[i] + nums[l] + nums[r];
if(s==0){
res.push_back({nums[i],nums[l],nums[r]});
while(l<r&&nums[l++]==nums[r]);
while(l<r&&nums[r--]==nums[r]);
}
else if(s>0)
r--;
else
l++;
}
}
return res;
}
vector<vector<int>> test2(vector<int>& nums){
//下面这是时哈希表做法 ,时间复杂度O(n^2)
vector<vector<int>> res;
sort(nums.begin(),nums.end()) ;
map<int,int> m;
for(int i=0;i<nums.size();i++){
m[nums[i]] = i;
}
for(int i=0;i<nums.size();i++){
if(nums[0]>0)
break;
if(i>0&&nums[i-1]==nums[i])
continue;
// map<int,int> m;//为啥map要放在循环体中
for(int j=i+1;j<nums.size();j++){
if(m.count(-nums[i]-nums[j])){
res.push_back({nums[i],nums[j],-nums[i]-nums[j]});
while(j+1<nums.size()&&nums[j]==nums[j++]);
}
// else
// m[nums[j]] = j; //为啥要这么赋值
}
}
return res;
}
bool test3(vector<int>& nums){
//字节问的是存在三数之和返回true即可
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++){
if(nums[0]>0)
return false;
int l=i+1,r=nums.size();
while(l<r){
int s = nums[i]+nums[l]+nums[r];
if(s==0)
return true;
else if(s>0)
r--;
else
l++;
}
}
return false;
}
//之前理解不行,下面这个是暂时的最终版本
bool test4(vector<int>& nums){
sort(nums.begin(),nums.end());
map<int,int> m;
for(int i=0;i<nums.size();i++){
m[nums[i]] = i;
}
for(int i=0;i<nums.size();i++){
// map<int,int> m;//每次都要新建一个map ,浪费
for(int j=i+1;j<nums.size();j++){
//if(m.find(-nums[i]-nums[j])!=m.end())
if(m.count(-nums[i]-nums[j]))
return true;
// else
// m[nums[j]] = j;//我丢,那天被绕进入了
}
}
return false;
}
int main(){
vector<int> a{2,0,-1,1,-1,-2};
vector<int> b{-1,-1,-1,2};
vector<vector<int> > res;
// b = test1(a);
res = test2(a);
// bool flag = test3(a);//双指针思路
bool flag = test4(b);//哈希思路
if(flag == true)
cout<<"yes"<<endl;
else
cout<<"No"<<endl;
for(int i=0;i<res.size();i++){
for(int j=0;j<res[0].size();j++){
cout<<res[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
结合上面注意下面的哈希会出现的问题,输出数组不够
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int len = nums.size();
vector<vector<int>> ans;
sort(nums.begin(), nums.end());
for (int i = 0; i < len; i++) {
if (i != 0 && nums[i] == nums[i - 1]) continue; // 跳过i代表的重复数字
unordered_map<int, int> m;
for (int j = i + 1; j < len; j++) {
if (m.count(-nums[i] - nums[j])) {
ans.push_back({ nums[i], nums[j], -nums[i] - nums[j] });
while (j + 1 < len && nums[j] == nums[j + 1]) j++; // 跳过j代表的重复数字
}
else m.insert(pair<int, int>(nums[j], j)); // 我们是在nums[i]后面的数字寻找nums[j]和nums[k](即-nums[i] - nums[j]),所以这里存储nums[j]
}
}
return ans;
}
};
下面这个三数之和是判断是否为0
排序+去重
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> res;
for(int i=0;i<nums.size();i++){
if(nums[0]>0)
break;
if(i>0&&nums[i-1]==nums[i])//去重
continue;
int l=i+1,r=nums.size()-1;
while(l<r){
int s = nums[i]+nums[l]+nums[r];
if(s==0){
res.push_back({nums[i],nums[l],nums[r]});
while(l<r&&nums[r--]==nums[r]);//去重
while(l<r&&nums[l++]==nums[l]);
}
else if(s>0)
r--;
else
l++;
}
}
return res;
}
};
八、位运算
1、二进制中1的个数(✅)
例:A = 0011 1100,A >> 2 将得到 15,即为 0000 1111
class Solution {
public:
int NumberOf1(int n) {
int t = 0;
for(int i=0;i<32;i++){
if(n>>i&1)
t++;
}
return t;
}
};
九、DFS
LRU算法(❌)
最近最久未使用算法(OS页面置换算法中的一种),(缓存算法)
下面这个方法用到了很多库函数
#include <unordered_map>
#include <list>
#include <vector>
using namespace std;
struct Node
{
Node(int k = 0, int v = 0) : key(k), val(v) {}
int key;
int val;
};
class Solution
{
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
cap = k;
vector<int> ans;
for (auto &input : operators)
{
if (input[0] == 1)
{
set(input[1], input[2]);
}
else
{
ans.push_back(get(input[1]));
}
}
return ans;
}
//删除
int remove(std::list<Node>::iterator &ite)
{
int key = ite->key;
int val = ite->val;
L.erase(ite);
H.erase(key);
return val;
}
// 添加
void add(int key, int val)
{
L.push_front(Node(key, val));
H[key] = L.begin();
if (L.size() > cap)
{
auto last = L.end();
--last;
remove(last);
}
}
void set(int x, int y)
{
auto ite = H.find(x);
//已经存在,删除了再添加到头部
if (ite != H.end())
{
remove(ite->second);
}
add(x, y);
}
int get(int x)
{
int val = 0;
//已经存在,删除了再添加到头部
auto ite = H.find(x);
if (ite != H.end())
{
val = remove(ite->second);
add(x, val);
return val;
}
return -1;
}
private:
std::list<Node> L;
std::unordered_map<int, std::list<Node>::iterator> H;
int cap;
};
开始初始化一个head节点与一个tail节点,方便以后插入节点和删除节点,中间放置操作的节点。
[1,1,1] 当我们遇到第一个set方法的时候 就需要插入到head 和tail 之间,
[1,2,2] 这时我们需要将新节点插入到head与node(1,1)之间。
[1,3,2] 添加到head后面;
[2,1] 发现已经有key=1对应的节点;则把Node(1,1)移动到head后面;
[1,4,4] 这时候发现节点的数量已经达到内存上限,则需要把最不常用的节点Node(2,2)删除,把新节点插入到head后面;
[2,2] 这时候查找节点发现没有,则返回-1;
还是用下面的这个方法吧,比较原生
#include <unordered_map>
struct DListNode{
int key, val;
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) return {};
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;
if(operators.size() == 0) return {};
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.find(key) == mp.end()){ // hashmap 中没找到
DListNode* node = new DListNode(key, val);
mp[key] = node;
if(this->size <= 0){
removeLast();
}
else{
this->size--;
}
insertFirst(node);
}
else{ // hashmap 中已经有了,也就是链表里也已经有了
mp[key]->val = val;
moveToHead(mp[key]);
}
}
int get(int key){
int ret = -1;
if(mp.find(key) != mp.end()){
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);
this->tail->pre->pre->next = this->tail; // remove the last node in dlist
this->tail->pre = this->tail->pre->pre;
}
void insertFirst(DListNode* node){
node->pre = this->head;
node->next = this->head->next;
this->head->next->pre = node;
this->head->next = node;
}
};