1.链表有无环判断
//快慢指针
bool LoopList_is( ListNode *List)
{
if(!List) return false;
ListNode *slowPointer,*fastpointer;
slowpointer = fastpointer = List;
while(slowpointer != null && fastpointer == null)
{
slowpointer = sloepointer -> next;
fastpointer = fastpointer -> next -> next;
if(slowpointer == fastpointer)
return true;
}
return false;
}
2.实现一个单例模式
概念:单例模式,即一个类只能被实例一次。即是一个类只能有一个实例化的对象,那么这个类就要禁止别人通过new出来,或者直接定义的出来。
class CAR
{
public:
CAR(){}
~CAR(){}
};//这就不是单例模式,它可以有多个实例化对象
CAR a;
CAR *b = new CAR;
创建方法:将类的构造函数私有化,同时通过定义一个类内部的public 访问函数来调用类 ,为了保证每次返回的是同一个对象,加上static关键字。
1、饿汉式:即类产生的时候就创建好实例对象,这是一种空间换时间的方式
2、懒汉式:即在需要的时候,才创建对象,这是一种时间换空间的方式
class CSingleton
{
private:
CSingleton()
{
}
static CSingleton *p;
public:
static CSingleton *getInstance()
{
if (p == NULL)
{
p = new CSingleton();
}
return p;
}
};
CSingleton *CSingleton::p = NULL;//懒汉式,在类对象实例化的时候才初始化
//CSingleton *CSingleton::p = new CSingleton();//饿汉式,在类对象实例化之前就初始化了
int main()
{
CSingleton *t = CSingleton::getInstance();
CSingleton *tt = CSingleton::getInstance();
cout << t << endl << tt << endl;
return 0;
} //饿汉式
总结:利用静态变量和私有化构造函数的特性来实现单例模式。搞一个静态的自身类指针,然后把构造函数私有化,这样new的时候就只能让本类中的成员调用,然后不择手段在类内部new出这个对象,并提供一种方法供外部得到这个对象的地址。
3.给一个字符串判断单词数
无论字符串是什么格式输入,并非只有中间有空格的情况。
#include<iostream>
using namespace std;
int main()
{
int count = 0;
int i = 0;
string str1;
cout << "please input the string:" << endl;
getline(cin, str1);
for (i = 0; i < str1.length(); i++)
{
if ((str1[i] >= 'a' && str1[i] <= 'z') || (str1[i] >= 'A' && str1[i] <= 'Z'))
{
count++;
while (((str1[i] >= 'a' && str1[i] <= 'z') || (str1[i] >= 'A' && str1[i] <= 'Z')) && str1[i] != '\0')
{
i++;
}
}
}
cout << count << endl;
return 0;
}
4.开方算法
//二分法开方
double sqrt1(double x)
{
if (x == 0) return 0;
double JINGDU = 0.0001;
double low = 0.0;
double high = x;
double mid = (low + high) / 2;
while ((high - low) > JINGDU)
{
if (mid * mid > x)
{
high = mid;
}
else {
low = mid;
}
mid = (low + high) / 2;
}
return mid;
}
//牛顿迭代法,推荐使用这种
double sqrt2(double n)
{
if (n == 0) return 0;
double last = 0.0;
double x = 1.0;
while (x != last)
{
last = x;
x = (x + n / x) / 2;
}
return x;
}
5.青蛙跳台阶
青蛙跳台阶: 一次可以跳一步或者两步
int GuapiJumpFloor(int n)//斐波拉契数列
{
int *vec = new int[n + 1];
vec[0] = 0;
vec[1] = 1;
vec[2] = 2;
for (int i = 3; i <= n; i++)
{
vec[i] = vec[i - 1] + vec[i - 2];
}
return vec[n];
}
6.常用排序(快排、归并、堆排序要写吐)
//快速排序 O(nlogn)
int GetIndex(vector<int> &arr, int left, int right)
{
if (left > right) return 0;
int curent = arr[left];
while (left < right)
{
while (left < right && curent <= arr[right])
right--;
arr[left] = arr[right];//挖坑 vec[right]
while (left < right && curent >= arr[left])
left++;
arr[right] = arr[left];//填坑 vec[right] 挖坑 vec[left]
}
arr[left] = curent;//填坑 vec[left]
return left;
}
void Quick_Sort(vector<int> &arr,int left,int right)
{
if (left > right) return;
if (left < right)
{
int index = GetIndex(arr, left, right);
Quick_Sort(arr, 0, index - 1);
Quick_Sort(arr, index + 1, right);
}
}
//归并
void Merge(int *arr,int left,int mid,int right)
{
if(left == right) return 0;
int i = left,j = mid + 1;
int length = right - left + 1;
int *arr1 = new int[length];
int index = 0;
while(left < right)
{
while(i < mid && j < right)
arr1[index++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
while(i < mid) arr1[index++] = arr[i++];
while(j < right) arr1[index++] = arr[i++];
for(int n = 0; n < length ; n++) arr[left++] = arr1[n];
}
}
void Merge_Sort(int *arr,int left,int right)
{
if(left == right) return 0;
int mid = left + (right - left) / 2;
Merge_Sort(arr,left,mid - 1);
Merge_Sort(arr,mid + 1,right);
Merge(arr,left,mid,right);
}
//堆排序
void heap(int *arr,int i,int length)
{
for(int left = 2*i+1; left < length; left = 2*left + 1)
{
int right =left +1;
if(right < length && arr[left] > arr[right])
{
left++;
}
if(arr[i] < arr[left])
{
int temp = arr[i];
arr[i] = arr[left];
arr[left] = temp;
i = left;
}
}
}
void heap_Sort(int *arr)
{
if(strlen(arr) == 0) return;
int length = strlen(arr);
for(int i = (length / 2) - 1; i >= 0; i--)
{
heap_Adjust(arr,0,length);
}
for(int i = length - 1; i > 0; i--)
{
swap(arr,0,i);
heap_Adjust(arr,0,i);
}
}
7.反转链表
将链表头尾指针相互交换。
//常规法
ListNode *ReverseList(ListNode *phead)
{
if (phead == NULL) return NULL;
ListNode *pCurent, *pre, *pNext;
pCurent = phead;
pre = NULL;
while (pCurent != NULL)
{
pNext = pCurent->next;
if (pNext == NULL)
{
pCurent->next = pre;
return pCurent;
}
pCurent->next = pre;
pre = pCurent;
pCurent = pNext;
}
}
//递归法
ListNode *ReversList(ListNode *List)
{
if(List == NULL || List->next == NULL)
{
return List;
}
ListNode *newList = ReversList(pHead->next);
pHead->next->next = pHead;// pHead 返回时在上一层。
pHead->next = NULL;
return newList;
}
8.两个链表,寻找公共节点
List FindFirstCommonNode(List list1, List list2)
{
List list_1 = list1;
int list1_length = 0, list2_length = 0;
while (list1 != NULL)
{
list1_length++;
list1 = list1->next;
}
while (list2_length != NULL)
{
list2_length++;
list2 = list2->next;
}
if (list2_length > list1_length)//如果2的长度大于1,那么交换,否则不换。
{
list1 = list2;
list2 = list_1;
}
int sub_length = list1_length > list2_length ? (list1_length - list2_length) :
(list2_length - list1_length);
//寻找长的数量,让长链表先走
for (size_t i = 0; i < sub_length; i++)
{
list1 = list1->next;
}
while (list1 != list2)
{
list1 = list1->next;
list2 = list2->next;
}
if (list1 == NULL) return 0;
else return list1;
}
9.查找字符串中不重复的最长子串
int LengthOfLongestSubstring(string s)
{
if (s.length() == 0) return 0;
//找到string中最大值,分配内存空间,防止内存浪费
int maxvalue = s[0], i = 0, j = 0;
int length = s.length();
for (i = 1; i < length; i++)
{
maxvalue = maxvalue > s[i] ? maxvalue : s[i];
}
bool *mark = new bool[maxvalue + 1];
for (i = 0; i <= maxvalue; i++)
{
mark[i] = false;//将标志位数组所有置为false
}
i = 0; j = 0;
int maxnum = 0;
while (i < length && j < length)
{
if (!mark[s[j]])
{
mark[s[j]] = true;
j++;
maxnum = maxnum > (j - i) ? maxnum : (j - i);//与之前的最大值比较,找到最大的字串长度
}
else
{
mark[s[i]] = false;
i++;
}
}
delete mark;
return maxnum;
}
10.LRU
缓存淘汰算法:LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。
敬请移步
11.手写求树的深度的代码
递归方式:
int GetDepthBitree(BiTree &T)
{
int Ldepth = 0;
int Rdepth = 0;
if (T == NULL)
{
return 0;
}
else
{
Ldepth = GetDepthBitree(T->Lchild);
Rdepth = GetDepthBitree(T->Rchild);
}
return Ldepth > Rdepth ? (Ldepth + 1): (Rdepth + 1);
}
12.手写生产者消费者
13.编程实现string类
class My_String
{
public:
My_String(const char *str);//普通构造函数
~My_String();//析构函数
My_String(const myString &s);//拷贝构造函数
My_String &operator = (const My_String &s);
private:
char *_str;
}
My_String::My_String(const char *str)//普通构造函数
{
if (str == NULL)
{
_str = new char[1];
_str = '\0';
}else {
int length = strlen(str);
_str = new char[length + 1];
my_strcpy(_str, str);
}
}
My_String::~My_String()//析构函数
{
while (_str != NULL)
{
delete[]_str;
}
}
My_String::My_String(const My_String &s)//拷贝构造函数
{
int length = strlen(s._str);
if (length == 0)
{
_str = new char[1];
_str = '\0';
}
else {
_str = new char[length + 1];
my_strcpy(_str,s._str);
}
}
My_String &My_String::operator = (const My_String &s)//符号重载函数
{
if(this == &s)
{
return *this;
}
delete []_str;
int length = strlen(s);
_str = new char[length + 1];
strcpy(_str,s._str);
return *this;
}
14.两个数组A,B,A有的B都有,求B-A;
我的思路1:1.先给两个数组排序,2.循环遍历,除掉重复即可,这样两次排序时间复杂度过高
思路2:找出两个数组中最大的值与最小的值,建立 最大值减去最小值的这样一个数组,全置为0,然后遍历两个数组,有重复元素的 i++,最后除掉 元素为2的值节课。
vector<int> VecBjianVecA(vector<int> &A, vector<int> &B)
{
if (A.size() == 0) return B;
int maxvalue = B[0];
for (int i = 1; i < B.size(); i++)
{
if (maxvalue < B[i])
maxvalue = B[i];
}
int *vec = new int[maxvalue + 1];
//memset(vec,0,sizeof(int)*4);
for (size_t i = 0; i < maxvalue+1; i++)
{
vec[i] = 0;
}
for (size_t i = 0; i < A.size(); i++)
{
vec[A[i]]++;
}
vector<int> res;
int j = 0;
for (size_t i = 0; i < B.size(); i++)
{
if (vec[B[i]] != 1)
{
res.push_back(B[i]);
}
}
return res;
}
15.输入一个数组,输出它的全排列,回溯法。
static vector<vector<int>> res;
static void backtrace(vector<int> &output, int first,int len)
{
if (first == len)
{
res.push_back(output);//递归到最后一步才将排列推送进最后的返回数组中
return;
}
for (int i = first; i < len; i++)
{
swap(output[i],output[first]);//作好选择之后,进入递归
backtrace(output, first + 1, len);
swap(output[i],output[first]);//递归之后,返回最初的状态,所以需要对之前的操作进行撤销,或者弥补。
}
}
vector<vector<int>> permute(vector<int> &arr)//返回全排列
{
backtrace(arr, 0, arr.size());
return res;
}
16.统计完全二叉树多少个节点
//采用 递归算法
int countNode(BiTree &T)
{
if(T == NULL) return 0;
int LNodeNum = countNode(T->Lchild);
int RNodeNum = countNode(T->Rchild);
return LNodeNum + RNodeNum + 1;
}
16.1 输出二叉树第K层的节点数量
注意:是第K层,不是前 k 层。
int countNode_k(BiTree &T,int k)
{
if(T == NULL && k < 1) return 0;
if(k == 1)
{
return 1;
}
int LNodeNum = countNode(T->Lchild,k--);
int RNodeNum = countNode(T->Rchild,k--);
return LNodeNum + RNodeNum;
}
16.2 二叉树叶子节点数量
int countNode_yezhi(BiTree &T)
{
if(T == NULL) return 0;
if(T->Lchild == NULL && T->Rchild == NULL)
{
return 1;
}
int LNodeNum = countNode(T->Lchild,k--);
int RNodeNum = countNode(T->Rchild,k--);
return LNodeNum + RNodeNum;
}
16.3 二叉树的非递归遍历
void Pre_OrderTraverseWithoutRecursion(BiTree &T)//非递归前序遍历
{
if (T == NULL) return;
TreeNode *p;
p = T;
stack<TreeNode*> sta;
while (!sta.empty() || p)
{
while (p)
{
cout << p->value << ' ';
sta.push(p);
p = p->Lchild;
}
if (!sta.empty())//非循环
{
p = sta.top();
sta.pop();
p = p->Rchild;
}
}
}
void Mid_OrderTraverseWithoutRecursion(BiTree &T)//非递归中序遍历 (迭代法遍历二叉树)
{
if (T == NULL) return;
TreeNode *p;
p = T;
stack<TreeNode*> sta;
while (!sta.empty() || p)//empty();检测是否为空,为空则返回 true,不为空返回 false ;
{
while (p)//先将左子树依次压入栈中,
{
sta.push(p);
p = p->Lchild;
}
if (!sta.empty())//该行判断,可以不需要
{
p = sta.top();
sta.pop();
cout << p->value <<' ';
p = p->Rchild;//等 stack 最后弹出结束后 p 会指向栈最后节点(即是根节点)的右节点,从而开始新的一轮左子树的压栈
}
}
}
void Back_OrderTraverseWithoutRecursion(BiTree &T)//非递归后序遍历
{
if (T == NULL) return;
TreeNode *pCurrentNode, *pLastNode;
pCurrentNode = T;
pLastNode = NULL;
stack<TreeNode*> sta;
while (pCurrentNode)//遍历左子树,将 pCurrentNode 定位
{
sta.push(pCurrentNode);
pCurrentNode = pCurrentNode->Lchild;
}
while (!sta.empty())
{
pCurrentNode = sta.top();
sta.pop();
//判断当前节点是否为根节点或者是否已被访问过。
if (pCurrentNode->Rchild == NULL || pCurrentNode->Rchild == pLastNode)
{
cout << pCurrentNode->value << ' ';
pLastNode = pCurrentNode;
}
else {
sta.push(pCurrentNode);
pCurrentNode = pCurrentNode->Rchild;
while (pCurrentNode)
{
sta.push(pCurrentNode);
pCurrentNode = pCurrentNode->Lchild;
}
}
}
}
17.strcpy、strlen、strcat、memcpy实现
char* my_strcpy(char *deststr, const char *str)//char*类型是为了方便后续的函数调用 链式连接
{
assert(str != NULL);
assert(deststr != NULL);//判断输入参数的合法性,如果条件为假,即终止程序。
char *res = deststr;
int length = strlen(str);
for (size_t i = 0; i <= length; i++)
{
deststr[i] = str[i];
}
//while ((*deststr++ = *str++)!='\0')
return res;
}
int my_strlen(const char *str)
{
if (str == NULL) return 0;
int i = 0;
while (str != '\0')
{
i++;
}
return i;
}
char* my_strcat(char *deststr, const char *str)//字符串连接函数,将字符串str连接到字符串deststr后面
{
assert(deststr != NULL && str !=NULL);
char *res = deststr;
while (deststr)
{
*deststr++;
}
while (str)
{
*deststr++ = *str;
}
*deststr = '\0';
return res;
}
void* my_memcpy(void *deststr, const void *str,size_t count)//从存储区str复制n个字符到deststr中去。
{
assert((deststr != NULL) && (str != NULL));
void *res = deststr;
while (count--)
{
*(char*)deststr = *(char*)str;
deststr = (char*)deststr + 1;
str = (char*)str + 1;
}
return res;
}
18 二分查找及其变种
常见的二分查找就是二叉树的性质,每次都查找剩余的一半。所以它的事件复杂度也即是O(logN)。虽然二分查找算法的效率很高(这也是二分查找算法被广泛应用的原因),但是前提就是说查找的序列:有序。在需要频繁进行插入或者删除操作的数据记录中使用二分查找算法效率不是很高,因为还要维持数据的有序还需要额外的排序开销。
int ErfenSearch(int *arr,int value)
{
if(arr == NULL) return 0;
length = sizeof(arr)/sizeof(int);
int low = 0;
int high = length-1;
while(low <= high)
{
mid = low + (low + high)/2;
if(arr[mid] == value) return mid;
if(arr[mid] < value) low = mid + 1;
if(arr[mid] > value) high = mid - 1;
}
}
变种:
1.插值查找算法
变种的地方主要在于mid值的计算方法。在有序序列当中,当我们所要查找的value偏小或者偏大,如果再用二分就会造成开销的过大。所以插值查找算法当中mid值的计算方法就是将其更偏向查找值value,已达到更高的效率。
二分查找中mid值计算:
插值查找算法中mid值计算:
int InsertionSearch(string str, char value)
{
int low, high, mid;
low = 0;
high = str.length()-1;
while (low <= high)
{
mid = low + (value -str[low])/(str[high]-str[low])*(high - low);//自适应的中值寻找,让中值跟家靠近顺序表中查找值的位置,相比二分更有效率
if (str[mid] == value)
return mid;
if (str[mid] > value)
high = mid - 1;
if (str[mid] < value)
low = mid + 1;
}
}
2.斐波拉契数列查找,根据黄金分割法来查找关键值,得到的是算法时间复杂度O(logN),但是效率比二分法还是要高一些。在该算法中的mid值接为:
//斐波那契查找
void Fibonacci(int * F)
{
F[0] = 0; F[1] = 1;
for (int i = 2; i < 20; ++i)
F[i] = F[i - 1] + F[i - 2];
}/*定义斐波那契数列*/
const int max_size = 20;//斐波那契数组的长度 /*构造一个斐波那契数组*/
int FibonacciSearch(int *arr, int key, int length)//a为要查找的数组,n为要查找的数组长度,key为要查找的关键字
{
int low = 0; int high = length - 1;
int F[max_size];
Fibonacci(F);//构造一个斐波那契数组F
int k = 0;
while (length > F[k])//计算n位于斐波那契数列的位置
{
k++;
}
int * temp;//将数组a扩展到F[k]-1的长度
temp = new int[F[k] - 1];
memcpy(temp, arr, length * sizeof(int));
for (int i = length; i < F[k] - 1; i++)
{
temp[i] = arr[length - 1];
}//将新构造的数组补充完整
while (low <= high)
{
int mid = low + F[k - 1] - 1;
if (key == temp[mid]) return mid;
if (key < temp[mid])
{
high = mid - 1;
k -= 1;
}
else if (key > temp[mid])
{
low = mid + 1;
k -= 2;
}
}
delete temp;
return -1;
}