面试题18:删除链表的节点
在O(1)时间内删除链表节点。
给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
}
void DeleteNode(List** pListHead, ListNode* pToBeDeleted);
常规的做法无疑是从链表的头节点开始,顺序遍历查找要删除的节点,并在链表中删除。
在单向链表中,节点中没有指向抢一个节点的指针,所以只好从链表的头节点开始顺序查找。
如果我们把下一个
void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted)
{
if(!pListHead ||!pToBeDeleted)
return;
//要删除的节点不是尾节点
if(pToBeDelete->m_pNext!=nullptr)
{
ListNode* pNext = pToBeDelete->m_pNext;
pToBeDeleted->m_nValue = pNext->m_nValue;
pToBeDeleted->m_pNext = pNext->m_pNext;
delete pNext;
pNext = nullptr;
}
}
//链表只有一个节点,删除头节点(也是尾节点)
else if(*pListHead == pToBeDeleted)
{
delete pToBeDeleted;
pToBeDeleted = nullptr;
*pListHead = nullptr;
}
else
{
ListNode* pNode = *pListHead;
while(pNode->m_pNext != pToBeDeleted)
{
pNode = pNode->m_pNext;
}
pNode->m_pNext = nullptr;
delete pToBeDelete;
pToBeDelete = nullptr;
}
}
把节点j的内容复制覆盖节点i,接下来再把节点i的m_pNext指向j的下一个节点,在删除节点j。
删除链表中重复的节点
在一个排序的链表中,如何删除重复节点?
确定删除函数的参数,输入需要带删除链表的头节点,头节点可能与后面的节点重复,也就是说头节点也有可能被删除,因此删除函数应该声明为void deleteDuplication(ListNode** pHead)
void DeleteDuplication(ListNode** pHead)
{
if(pHead == nullptr ||*pHead == nullptr)
return;
ListNode* pPreNode = nullptr;
ListNode* pNode = *pHead;
while(pNode != nullptr)
{
ListNode* pNext = pNode->m_pNext;
bool needDelete = false;
if(pNext!=nullptr&&pNext->m_nValue==pNode->m_nVaule)
needDelete = true;
if(!needDelete)
{
pPreNode = pNode;
pNode = pNode->m_pNext;
}
else
{
int value = pNode->m_nValue;
ListNode* pToBeDel = pNode;
while(pToBeDel != nullptr && pToBeDel->m_nValue == value)
{
pNext = pToBeDel->m_pNext;
delete pToBeDel;
pToBeDel = nullptr;
pToBeDel = pNext;
}
if(pPreNode == nullptr)
*pHead = pNext;
else
pPreNode -> m_pNext=pNext;
pNode=pNext;
}
}
}
面试题19:正则表达式匹配
请实现一个函数用来匹配包含’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次。
bool match(char* str,char* pattern)
{
if(str == nullptr||pattern == nullptr)
return false;
return matchCore(str,pattern);
}
bool matchCore(char* str,char* pattern)
{
if(*str == '\0'&&*pattern == '\0')
return true;
if(*str != '\0'&&*pattern =='\0')
return false;
if(*(pattern+1) =='*')
{
if(*pattern==*str||(*pattern == '.'&& *str!='\0'))
return matchCore(str+1,str+2)
||matchCore(str+1,pattern)
||matchCore(str,pattern+2);
else
return matchCore(str,pattern+2);
if(*str == *pattern||(*pattern == '.'&&*str!= '\0'))
return matchCore(str+1,pattern+1);
return false;
}
}
请实现一个函数用来判断字符串是否表示数值(包含整数和小数)
bool isNumeric(const char* str)
{
if(str == nullptr)
return false;
bool numeric = scanInteger(&str);
if(*str =='!')
{
++str;
numeric = scanUnsignedInteger(&str)||number;
}
if(*str == 'e'||*str == 'E')
{
++str;
numeric = numeric&&scanInteger(&str);
}
return numeric && *str =='\0';
}
bool scanUnsignedInteger(const char** str)
{
const char* before = *str;
while(** str!='\0'&& **str>='0'&&**str<='9')
++(*str);
return *str>before;
}
bool scanInteger(const char** str)
{
if(**str == '+'||**str =='-')
++(*str);
return scanUnsignedInteger(str);
}
全面考虑数值整数、小数、指数部分的特点
调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
双指针 :第一个指针初始化时指向数组的第一个数字,它只想后移动,第二个指针初始化时指向数组的最后一个数字,它只向前移动。
void ReorderOddEven(int *pData,unsigned int length)
{
if(pData == nullptr||length ==0)
return ;
int*pBegin = pData;
int*pEnd = pData+length-1;
while(pBegin<pEnd)
{
//向后移动pBegin,直到它指向偶数
while(pBegin<pEnd&&(*pBegin & 0x1)!=0)
pBegin++;
while(pBegin<pEnd && (*pEnd &0x1)==0)
pEnd--;
if(pBegin<pEnd)
{
int temp = *pBegin;
*pBegin = *pEnd;
*pEnd = temp;
}
}
}
考虑可扩展的解法
void Reorder(int *pData,unsigned int length ,bool(*func)(int))
{
if(pData == nullptr||length ==0)
return;
int *pBegin = pData;
int *pEnd = pData+length-1;
while(pBegin<pEnd)
{
while(pBegin<pEnd && !func(*pBegin))
pBegin++;
while(pBegin < pEnd&&func(*pEnd))
pEnd--;
if(pBegin<pEnd)
{
int temp = *pBegin;
*pBegin = *pEnd;
*pEnd = temp;
}
}
}
bool isEven(int n)
{
return (n&1)==0;
}
void ReorderOddEven(int *pData,unsigned int length)
{
Reorder(pData,length,isEven);
}