< 记录下算法题 个人复习使用 >
数据结构
注:有些结构体指针方便书写我使用符号点’.'取值
<字符串>
1.剑指offer19题:正则匹配
思路:
如果当前字符后面字符后跟着*字符,则会出现几种情况:
当前所指的字符相同时 :1.字符串保持不变,模式后移两位(出现零次);2.字符串后移一位,模式串后移两位(出现一次);3.字符串后移一位,模式串保持不变(出现多次)
当前所指字符不同是:字符串保持不变,模式后移两位
bool matchCore(char *str, char *pat)
{
if(*str == 0 && *pat == 0)
return true;
if(*str != 0 && *pat == 0)
return false;
//next is *
if(*(pat+1) == '*' )
{
if(*pat == *str)
{
return matchCore(str, pat+2) || matchCore(str+1, pat+2) || matchCore(str+1, pat);
}
else
{
return matchCore(str, pat+2)
}
}
if(*str == *pat || (*pat == '.' && *str != 0))
return matchCore(str+1, pat+1)
return false;
}
2.字符串的排列组合
排列思路:
1.将字符串分为两部分,第一部分为第一个字符,第二个部分为后面的所有字符
2.重复1继续对后面的所有字符做全排
3.如果需要对重复字符处理,则只需要增加一个判断重复函数
//permutation(str, str); call permutation function
void permutationCore(char*sBegin, char *str)
{
if(sBegin == 0)
{
//show str
return;
}
for(char* Cstr = sBegin; *Cstr != '\0'; Cstr++)
{
//jugde repetition
swap(*Cstr, *sBegin);
permuatationCore(sBegin+1, str);
swap(*Cstr, *sBegin);
}
}
组合思路:
1.将字符串分为第一个字符和后面其他字符两部分
2.m个字符串取长度为n(0<=n<=m)的排列; 如果包含第一个字符,则后面n-1中取m-1个;不包含第一个字符,后面n-1中取m个
void combine(char str[], int lenth)
{
vector<char> data;
for (int i = 1; i <= lenth; i++)
{
combineCore(str, i, data);
}
}
void combineCore(char str[], int n, vector<char> data)
{
if (n == 0)
{
for (vector<char>::iterator it = data.begin(); it != data.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
return;
}
if (*str == '\0') return;
data.push_back(*str);
//m-1中取n-1个
combineCore(str + 1, n - 1,data);
data.pop_back();
//m-1中取n个
combineCore(str + 1, n,data);
}
举一反三:
如果要求摆放若然数字,只需要将数字的所有排列都求出来,然后依次判断每个排列是否满足要求。
3.字符串的翻转
思路:先翻转整个字符串,然后逐个翻转每个单词
void reverse(char *pbegin, char *pend);
char * reverseWord( char * data)
{
//reverse whole string
reverse(data, data + len(data));
//reverse word
char *pbegin = data, *pend = data;
while(*pbegin != 0)
{
if(*pbegin == ' ')
{
pbegin++;
pend++;
}
else if (pend == ' ')
{
reverse(pbegin, pend - 1);
pbegin = pend + 1;
}
else
{
pend++;
}
}
}
T2:字符串的左转
思路:将N位左转,先把字符串以N位分为两部分,分别旋转两个字符串,然后旋转整个字符串;
<链表>
1.删除单链表的节点
在O(1)的时间复杂度删除链表的节点
思路:
struct node{
int val;
struct node *next;
}
pb为需要删除的链表;
pb->val = pb->next->val; pb->next = pb->next->next;
del 之前pb的下一个节点
注意点:
1.如果删除节点位于尾部(使用O(n)的方法处理)
2.删除链表中重复节点
思路:三个指针,一个指向当前节点,一个指向下一个节点,一个指向上一个节点
注意点:重复节点可能位于头部、中间、尾部;可能存在多个重复节点
3.链表中倒数第N个节点
思路:两个指针,并且这两个指针的步长保持k - 1
注意点:链表的长度是否大于N
举一反三:链表的中间节点问题等,只要是相关类似问题都可以采用双指针步长
4.链表中找环的起点
思路:
1.确定链表中存在环
2.确定环的大小
3.确定环的起点
5.链表的翻转
思路:使用三个指针、使用递归
注意点:翻转前链表的尾指针会变成翻转后的尾指针,需要保存
6.复杂链表的复制(链表节点有一个指向任意节点的指针)
思路:
1.创建复制节点,并将创建的每一个复制节点都挂载原始节点的后面,即
N’->next = N->next;
N->next = N’;
2.赋予复制节点的任意节点值,等价于N’->rand = N->rand->next;
3.分割两个链表
7.两个单链表公共节点
思路:
思路1.使用栈来做辅助空间
思路2.确定两个链表的长度,长的链表比短的链表先走(长-短)步
<树>
基本概念
(1)二叉搜索树不一定是完全二叉树,因此出现平衡二叉搜索树这个概念;二叉搜索树的左节点小于根节点,右节点大于根节点。
(2)完全二叉树和满二叉树,满二叉树最后一层也须是满叶子节点。
1.重建二叉树
根据前序遍历和后续遍历的集合,构建二叉树。
思路:采用递归的方式 core(preS, preE, inoS, inoE)
递归的终止条件 :前序集合只有一个变量,中序集合只有一个变量,且两个集合的变量相等。
//伪代码
BinaryTree* ConstructCore(
int *preSta, int *preEnd,
int* inordSta, int *inordEnd)
{
int rootValue = preSta[0];
BinaryTree *root = new BinaryTree();
root->value = rootValue;
root->left = root->righ = nullptr;
if (preSta == preEnd)
{
if (inordSta == inordEnd && *inordSta == *preSta)
{
return root;
}
else
{
//err
}
}
int *t = inordSta;
while (t <= inordEnd && *t != rootValue)
t++;
int leftLen = t - inordSta;
int *leftPreEnd = inordSta + leftLen;
if (leftLen > 0 /*left*/)
{
root->left = ConstructCore(preSta+1, leftPreEnd, inordSta, t - 1 );
}
if (1/*right*/)
{
}
return root;
}
2.二叉树的下一个节点
思路:三种情况,若节点有左子树,若节点有右子树,若节点没有子树(又分为为父节点的右孩子和父节点的左孩子)
3.a树是否为b树的子结构
bool trasfer(Tree *t, Tree *tm)
{
if(t == NULL && tm == NULL)
return false;
if(equal(t.v,tm.v))
bool hasSub = hasSubNode(t, tm)
if<