1.单链表反转
1.使用三个指针遍历单链表,逐个链接点进行反转。
Node * ReverseList(Node *head)
{
Node *p1,*p2,*p3;
if(head == NULL || head->next == NULL)
return head;
p1=head;
p2=p1->next;
while(p2) //注意条件
{
p3=p2->next; //要改变p2->next的指针,所以必须先保留p2->next
p2->next=p1;
p1=p2; //循环往后
p2=p3;
}
head->next=NULL; //原先的head已经变成tail,别忘了置空,只有到这步才能置空
*head=p1;
return head;
}
2.从第2个节点到第N个节点,依次逐节点插入到第1个节点(head节点)之后,最后将第一个节点挪到新表的表尾。
Node* ReverseList(Node* head)
{
Node *p,*q;
p=head->next;
while(p->next!=NULL) //在这个循环过程中p所指的元素一直是不变的
{
q=p->next;
p->next=q->next;
q->next=head->next;
head->next=q;
}
p->next=head; //相当于成环
head=p->next->next; //新head变为原head的next
p->next->next=NULL; //断掉环
return head;
}
}
2.top K 问题
1.类选择排序法
算法思路:对目标序列N个数遍历,取出其中最大的数最为Top1;再次遍历剩下的N-1个数,取出其中最大的数为Top2;…再对剩下的N-K+1个数遍历,取出其中最大的数为TopK,这样就可以找到最大的K个数了。
vector<int> TopKBySelect(vector<int>& nums,int k,int len)
{
vector<int>res;
vector<int>flag(len);
for(int i=0;i<k;i++)
{
int maxIndex=0; //保存最大数的索引
int maxNum=nums[0]; //保存最大数
for(int j=0;j<len;j++)
{
if(nums[j]>maxNum&&!flag[j]) //如果大于最大数并且没有被取出来过
{
maxNum=nums[j];
maxIndex=j;
}
}
flag[maxIndex]=-1; //将此次遍历的最大数索引标记为-1,放置再次被取出
res.push_back(maxNum); //存入该最大数
}
return res;
}
2.快速排序法
在快速排序中,每一轮排序都会将序列一分为二,左子区间的数都小于基准数,右子区间的数都大于基准数,而快速排序用来解决TopK问题,也是基于此的。N个数经过一轮快速排序后,如果基准数的位置被换到了i,那么区间[0,N-1]就被分为了[0,i-1]和[i+1,N-1],这也就是说,此时有N-1-i个数比基准数大,i个数比基准数小,假设N-1-i=X那么就会有以下几种情况:
①X=K。这种情况说明比基准数大的有K个,其他的都比基准数小,那么就说明这K个比基准数大的数就是TopK了;
②X<K。这种情况说明比基准数大的数不到K个,但是这X肯定是属于TopK中的TopX,而剩下的K-X就在[0,i]之间,此时就应当在[0,i]中找到Top(K-X),这就转换为了TopK的子问题,可以选择用递归解决;
③X>K。这种情况说明比基准数大的数超过了K个,那么就说明TopK必定位于[i+1,N-1]中,此时就应当继续在[i+1,N-1]找TopK,这样又成了TopK的一个子问题,也可以选择用递归解决。
int getIndex(vector<int>& nums,int left,int right) //快排获取相遇点(基准数被交换后的位置)
{
int base=nums[left];
int start=left;
while(left<right)
{
while(left<right&&nums[right]>=base)right--;
while(left<right&&nums[left]<=base)left++;
int temp=nums[right];
nums[right]=nums[left];
nums[left]=temp;
}
nums[start]=nums[left];
nums[left]=base;
return left;
}
int findTopKthIndex(vector<int>&nums,int k,int left,int right)
{
int index=getIndex(nums,left,right); //获取基准数位置
int NumOverBase=right-index; //比基准数大的数的个数
if(NumOverBase==k)return index; //比基准数大的刚好有K个
//比基准数大的多于K个,就在右边子区间寻找TopK
else if(NumOverBase>k)return findTopKthIndex(nums,k,index+1,right);
//比基准数大的少于K个,就在左边找剩下的
return findTopKthIndex(nums,k-NumOverBase,left,index);
}
vector<int> TopKInQuick(vector<int>& nums,int k,int len)
{
if(len==k)return nums;
vector<int>res;
vector<int>temp(nums.begin(),nums.end()); //TopK不对原数组改变
int index=findTopKthIndex(temp,k,0,len-1); //通过快排找到第K+1大的数的位置
for(int i=len-1;i>index;i--)res.push_back(temp[i]); //取出TopK返回
return res;
}
3.堆排序法(最优解)
堆排序是通过维护大顶堆或者小顶堆来实现的。堆排序法来解决N个数中的TopK的思路是:先随机取出N个数中的K个数,将这N个数构造为小顶堆,那么堆顶的数肯定就是这K个数中最小的数了,然后再将剩下的N-K个数与堆顶进行比较,如果大于堆顶,那么说明该数有机会成为TopK,就更新堆顶为该数,此时由于小顶堆的性质可能被破坏,就还需要调整堆;否则说明这个数最多只能成为Top K+1 th,因此就不用管它。然后就将下一个数与当前堆顶的数作比较,根据大小关系如上面所述方法进行操作,知道N-K个数都遍历完,此时还在堆中的K个数就是TopK了。
void adjustMinHeap(vector<int>& nums,int root,int len) //小顶堆结点调整
{
int lch=2*root+1; //左子结点
int rch=lch+1; //右子结点
int index=root; //较大结点
if(rch<len&&nums[rch]<nums[index])index=rch;
if(lch<len&&nums[lch]<nums[index])index=lch;
if(index!=root) //当前结点非最小结点
{
swap(nums[index],nums[root]);
adjustMinHeap(nums,index,len);
}
return;
}
vector<int> TopKInHeap(vector<int>& nums,int k,int len)
{
vector<int>res(nums.begin(),nums.begin()+k); //取出前k个数
for(int i=k/2-1;i>=0;i--) //根据前K个数建立一个小顶堆
{
adjustMinHeap(res,i,k);
}
//将剩下的数与堆顶做比较
for(int i=k;i<len;i++)
{
if(nums[i]>res[0]) //当前数比堆顶数大
{
res[0]=nums[i]; //将堆顶更新为该数
adjustMinHeap(res,0,k); //重新调整堆
}
}
return res;
}
3.两两反转单链表(不能改变节点的值)
1.递归
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode* newHead = head->next;
head->next = swapPairs(newHead->next);
newHead->next = head;
return newHead;
}
};
}
2.交换节点
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr) {
ListNode* tmp = cur->next; // 记录临时节点
ListNode* tmp1 = cur->next->next->next; // 记录临时节点
cur->next = cur->next->next; // 步骤一
cur->next->next = tmp; // 步骤二
cur->next->next->next = tmp1; // 步骤三
cur = cur->next->next; // cur移动两位,准备下一轮交换
}
return dummyHead->next;
}
};
4.常见c语言库函数实现
1.strcpy
//strcpy
//方法一:
char* Strcpy1(char* dst, const char* src){
assert(dst != NULL && src != NULL);
for (int i = 0; src[i] != '\0'; ++i){
dst[i] = src[i];
}
dst[i] = '\0';
return dst;
}
//方法二:
char* Strcpy2(char* dst, const char* src){
assert(dst != NULL && src != NULL);
char *tmp = dst;
while ((*tmp++ = *src++) != '\0');
*tmp = '\0';
return dst;
}
2.memcpy
//memcpy
void *Memcpy(void *dest, const void *src, size_t count){
char *d;
const char *s;
if (dest > (src+size)) || (dest < src)){
d = dest;
s = src;
while (count--)
*d++ = *s++;
}else{ //内存重叠情况
d = (char *)(dest + count - 1);
s = (char *)(src + count -1);
while (count --)
*d-- = *s--;
}
return dest;
}
3.strcat
//strcat
char* Strcat(char* dst, const char* src){
assert(dst != NULL && src != NULL);
char* tmp = dst;
while (*tmp)
tmp++;
while ((*tmp++ = *src++) != '\0');
return dst;
}
4.strcmp
//strcmp
int Strcmp(const char* str1, const char* str2){
assert(str1 && str2);
while (*str1 == *str2 && *str2)
{
str1++;
str2++;
}
return *(unsigned char*)str1 - *(unsigend char*)str2;
}
5.删除链表的倒数第K个节点
1.ListNode *p=*q=head
2.q指针后移k个位置
3.while q != end, p,q同时后移
4. 删除p的下一个节点
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *pre = head, *end = head;
while(n--){
end = end->next;
}
while(end && end->next){
pre = pre->next;
end = end->next;
}
if(end == NULL){
return head->next;
}else{
ListNode *tmp = pre->next;
pre->next = tmp->next;
delete tmp;
}
return head;
}