C/C++/linux易忘知识点(偏算法)

本文详细介绍了链表的基本操作,包括单链表的反转和两两节点的交换,并提供了具体的实现代码。此外,还深入探讨了TopK问题的不同解决方法,包括选择排序、快速排序和堆排序等算法,并给出了每种方法的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值