编程题算法总结

求最大公约数 最小公倍数

最大公约数

辗转相除法

大的a除小的b,得到余数如果是0,那么b就是最大公约数,否则就取余数做那个小的,本来的b就成了大的继续操作。

    int n,m;
    //辗转相除法,ab最大公约数 = ab余数和b的最大公约数
    int yu,a,b;
    a = n>m?n:m;
    b = n>m?m:n;
    while(1)
    {
      yu = a % b;
      if(yu == 0) break;
      a = b;
      b = yu; //已经是余数了,一定比b小
    }
 //b就是

更相减损法

大的a 小的b,a-b=0就找到了,否则a = a-b循环

    int a,b;
    int cal = a*b;
    //更相减损法 ,a = a-b
    while(a!=b)
    {
      if(a>b)
      a = a-b;
      else b = b-a;
    }
 //b就是

最小公倍数

偷懒法

先求最大公约数,由最大公约* 最小公倍数 = a* b

迭乘法

最小公倍数一定是其中某个数的n倍


    int a,b;
    int cal = a*b;
    int i = 1;
    //迭乘法求最小公倍数
    while(a*i%b!=0)
    {
      i++;
    }
//a*i就是

正序数组插值

    int arr[10],x;
    //正序数组和要插入的值
    int i = 8;
    while(i>=0&&arr[i]>x)  //注意从后面开始遍历,移动到后面的那个
    {
        arr[i+1] = arr[i];
        i--;
    }
    arr[i+1] = x;
    for (int i = 0; i < 10; i++)
    {
        printf("%d\n",arr[i]); //打印输出
    }

以上其实是插入的算法,如果是删除,需要从前向后遍历,然后把后面的元素往前移

编辑距离

设A和B是两个字符串。我们要用最少的字符操作次数,将字符串A转换为字符串B。这里所说的字符操作共有三种:

  1. 删除一个字符;
  2. 插入一个字符;
  3. 将一个字符改为另一个字符。
    对任给的两个字符串A和B,计算出将字符串A变换为字符串B所用的最少字符操作次数

> 三种操作分别是:插入 删除 替换

需要清楚的点:

  1. 两字符串A和B,给A插入相当于给B删除,反之亦然(例如cat和cate)

  2. 替换A相当于替换B(例如cat和fat)

  3. 故本质操作就三种 : ① A插 ② B插 ③ 替换

dp[i][j]表示从 A 的前 i 个 字符转换到 B 的 前 j 个 字 符所需的最短步数

(例如A是qwert B是aserty 那么最终输出结果就是dp[5][6])

  • dp[i-1][j-1]到dp[i][j]需要进行替换操作。若A的第 i 位(其实就是当前操作位)等于 B的第 j 位(当前操作位),那么就不需要替换,因此dp[i][j] = dp[i-1][j-1] ,否则dp[i][j] = dp[i-1][j-1]+1

  • dp[i-1][j]到d[i][j]需要进行删除操作。dp[i][j] = dp[i-1][j] + 1

  • dp[i][j-1]到d[i][j]需要进行添加操作。dp[i][j] = dp[i][j-1] + 1

★当A与B末位相同时:

dp[i][j] = min(dp[i][j−1]+1,dp[i−1][j]+1,dp[i−1][j−1])

★当A与B末位不相同时:

dp[i][j] =min(dp[i][j−1]+1,dp[i−1][j]+1,dp[i−1][j−1]+1)

那么,我们要怎么从dp[0][0] 到 dp[i][j]呢?

很简单,每一步做出判断,替换、删除、添加哪个短就选哪个!

这里需要注意

  • dp[a][b]代表着前面所有步数的最短选择!
  • 由于不知道AB长度,因此A插和B插不可以混为一谈!

注意事项:

边界情况,A或B为空,直接输出不为空的数组长度即可

#include <stdio.h>

using namespace std;

int main(){

    //注意!!数组是从0开始索引的,所以第i位就是i-1的索引,以此类推!!

    string a,b;

    getline(cin,a);

    getline(cin,b);

    int len1 = a.length();

    int len2 = b.length(); //接受输入记录长度


    int dp[len1][len2]; //定义dp数组


    for (int i = 0; i < len1; i++) {dp[i][0] = i;}

    for (int j = 0; j < len2; j++) {dp[0][j] = j;}  //边界情况,同时也是初始化



    for (int i = 1; i < len1; i++)

    {

        for (int j = 1; j < len2; j++) //ij从1开始,因为已经初始化过了,从0开始会溢出

        {

            if(a[i-1] == b[j-1]) //A的第i位与B的第j位相同的情况

            dp[i][j] = min(min(dp[i][j-1]+1, dp[i-1][j]+1),dp[i-1][j-1]);

            else

            dp[i][j] = min(min(dp[i][j-1]+1, dp[i-1][j]+1),dp[i-1][j-1]+1);
        }

    }

    cout<<dp[len1-1][len2-1]<<endl;

    return 0;
}

寻找回文子串

#include<iostream>
using namespace std;
int main()
{
    string s;
    while(getline(cin,s))
    {
    int len = s.length();
    int dp[len][len]; //dp[i][j]表示i-j段是否为回文,是的话就表示长度 否的话就是0

    //从i处,一个一个改变终点直到结尾,是否存在回文?不存在的话,i往前走一个重复上述操作。
    //那么,怎么判断某段是否是回文呢?
    //ij相等 是回文 例如a 
    //ij差一且si=sj,比如aa,是。若si≠sj,不是。
    //ij差大于1,且si=sj,比如abcba,此时看s[i+1] s[j-1]是否为回文,
    //若i从开头开始遍历,会出现abeda这种,虽然a=a,但是bed是否为回文并没有判断!!!!

    int maxlen = 1;

    for(int i = len-1; i >= 0; i--){
        for(int j = i; j < len; j++){ 
            if(i == j)
            {
                //maxlen = 0;
                dp[i][j] = 1;
            }

            if(s[i]==s[j] && j==i+1){
                maxlen=max(maxlen,2);
                dp[i][j] = 1; //记录一下i-j是回文串
            }

            if(s[i]==s[j] && j>i+1 && dp[i+1][j-1]){
                maxlen = max(maxlen,j-i+1); //一定要注意写max!!!!
                dp[i][j] = 1;
            }
        }
    }cout<<maxlen<<endl;
    }
    return 0;
}

排序

冒泡

每一趟排好一个数,一共要排n趟,每趟遍历n-i个数,因为前i个已经在前i趟排好了!!

void BubbleSort_V1( int data[], int n )
{
	int i, j, k, t;
	bool flag;//提前结束排序的标志,1表示提前结束 
	for( i = 1; i <= n-1; i++ )
	{
		flag = 1;
		for( j = 1; j <= n-i; j++ )
		{
			if( data[j+1] < data[j] )
			{
				flag = 0; 
				t         = data[j+1];
				data[j+1] = data[j];
				data[j]   = t;
			}
		}
		
		if( flag == 1 )  break;

	}
}

快排

是对冒泡的改进

  • 先从数列中取出一个数作为基准数。
  • 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
  • 再对左右区间重复第二步,直到各区间只有一个数。
    image

注意!!!swap必须传入指针变量才能当api调用!!!

选择排序

跟冒泡很相近,区别在于选择排序一轮只排一个数,比如找到最小的放在第一个位置

#include<stdio.h>
int main(){
    int arr[5] = {9,7,4,8,6};
    for(int i = 0; i < 4; i++)
    {
        for (int j = i + 1; j < 5; j++)
        {
            if(arr[i]<arr[j])
            {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                //printf("%d %d   ",i,arr[i]);
            }
        }
    }//printf("\n");
    for (int i = 0; i < 5; i++)
    {
        printf("%d ",arr[i]);
    }
    return 0;
}

很他妈神奇,j从0的话,就是正序排,是因为j从0就相当于固定j

链表

初始化和尾插

初始化其实就是新建个头节点然后分配内存再置空

Node* initLinkedList() {
    Node* head = (Node*)malloc(sizeof(Node)); // 为头节点分配内存
    if (head == NULL) {
        printf("内存分配失败");
        exit(1);
    }
    head->next = NULL; // 只有这句是有用的!!!!!!!!!!
    return head;
}

区别

  • Node* head = NULL;:这是将头节点的指针初始化为 NULL,表示链表为空,没有任何节点。

  • Node* head = (Node*)malloc(sizeof(Node));:这是为头节点分配了内存空间,并将指针指向这块内存空间。此时头节点并不为空,而是一个指向已分配内存的节点指针。然而,此时头节点还没有与其他节点链接在一起,所以 head->next 设置为 NULL。

  • 总结起来,Node* head = NULL; 表示链表为空,而 Node* head = (Node*)malloc(sizeof(Node)); 表示链表只有一个头节点,但还没有其他节点与其链接。

在实际使用中,通常会将 Node* head = (Node*)malloc(sizeof(Node)); 与后续的插入操作写在一起!!!

#include<stdio.h>
#include<stdlib.h>
typedef struct Student{
    double id;
    double score;
    struct Student* next;
}Node;

//传入指向头指针(头指针指向头节点)的指针(因为要改变指向头节点的头指针)、要插入的值
//如果传入Node*head,只能改变Node head(注意不是Node*head),head只是一个结构体,已经不是一个指向结构体的指针了!!!!
void AddatEnd(Node** head, int id, int score)
{
    Node* new_node = (Node*)malloc(sizeof(Node));//固定写法,背下来吧
    new_node->id = id;
    new_node->score = score;
    new_node->next = NULL;  //记得给next赋值NULL
    Node *cur = *head;     //头节点还需要传出,不可以随意更改,因此使用临时节点遍历

    if(*head ==NULL)
    {
        *head = new_node;  //头指针为空,意思是没有头节点(头指针指向头节点,头指针存放的地址就是头节点这个结构体的地址)
        return;
    }
    while(cur->next!=NULL)
    {
        cur = cur->next;
    }
    cur->next = new_node;
}
void Print(Node *head)
{
    Node *cur = head;
    while(cur!=NULL)
    {
        printf("%.0lf,%.0lf\n",cur->id,cur->score);
        cur = cur->next;
    }
    printf("\n");
}

//返回值为void,所以对入参操作时不返回,要想改变头指针,只有通过二级指针改变。
//返回值为Node*,对入参操作后直接返回给原函数,不用通过二级指针修改
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    Node *head1 = NULL;
    double c1,c2;
    while(n--)
    {
        scanf("%lf%lf",&c1,&c2);
        AddatEnd(&head1,c1,c2);

    }
    Node *head2 = NULL;
    while(m--)
    {
        scanf("%lf%lf",&c1,&c2);
        AddatEnd(&head2,c1,c2);
    }
    Print(head1);
    Print(head2);
    return 0;
}

如果写Node* head = NULL,那么插入操作时就要判断* head是否为NULL,是的话把新建的节点赋给他

如果写Node* head= (Node*)malloc(sizeof(Node))并判空,赋值head->next = NULL,就不用判空,直接插入就行

head->next不是头指针的数据域!!!而是head指向的头节点的数据域!!!!!

cur = cur->next并不会改变链表结构!!!因为本来就是指向next的!!!

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Node{
    int data;
    struct Node* next;
}Node;

//初始化插值
void AddAtEnd(Node **head,int data){
    Node* new_node = (Node*)malloc(sizeof(Node));
    new_node->data = data;
    new_node->next = NULL;
    if(*head == NULL)
    {
        *head = new_node;
       // printf("%d",new_node->data);
        return;
    }
    Node*cur = *head;
    while(cur->next!=NULL)
    {
        cur = cur->next;
    }
    cur->next = new_node;
}

//get,获取链表第a个元素
void GetNode(Node *head, int a){
    if(a<=0&&head==NULL) //链表为空或者获取第-100个节点,都不合理
    {
        printf("get fail\n");
        return;
    }
    a--;
    Node *cur = head;
    while(a&&cur->next!=NULL)//先a到头还是先next到头?先a说明可以获取,先next说明a大了
    {
        cur = cur->next;
        a--;
    }
    if(a==0) printf("%d\n",cur->data);//a为0了,说明找到了,边界情况a=1也是满足的
    else 
    {
        printf("get fail\n");
    }

}


//delete删除某节点
void DeleteNode(Node **head, int a){
    if(a<=0&&(*head == NULL))
    {
        printf("delete fail\n");
        return;
    }
    if(a==1)
    {
        Node *temp = *head;
        *head = (*head)->next;
        free(temp);
        printf("delete OK\n");
        return;
    }
    Node *cur = *head;
    Node *prev = NULL;

    a--;
    while(a&&cur->next!=NULL)//先a到头还是先next到头?先a说明可以获取,先next说明a大了
    {
        prev = cur;
        cur = cur->next;
        a--;
    }
    if(a==0) 
    {
        prev->next = cur->next;//a为0了,说明找到了
        printf("delete OK\n");
    }
    else 
    {
        printf("delete fail\n");
    }

}


//Insert指定位置a插入b
void InsertNode(Node **head, int a, int b){


    if(a<=0)
    {
        printf("insert fail\n");
        return;
    }
    Node*temp = (Node*)malloc(sizeof(Node));
    temp->data = b;
    temp->next = NULL;


    if(*head ==NULL&&a==1)
    {
        *head = temp;
        printf("insert OK\n");
        return;
    }
    else if(a ==1) //头插法
    {
        temp->next = *head;
        *head = temp;
        printf("insert OK\n");
        return;
    }

    
    a--;
    Node *cur = *head;
    Node *prev = NULL;

    
    while(a&&cur->next!=NULL)//先a到头还是先next到头?先a说明可以获取,先next说明a大了
    {
        prev = cur;
        cur = cur->next;
        a--;
    }
    if(a==0) 
    {
        temp->next = cur->next;
        prev->next = temp;
        printf("insert OK\n");
    }
    else 
    {
        printf("insert fail\n");
    }
}

void showNode(Node *head){
    if(head ==NULL)
    {
    printf("Link list is empty\n");
    return;
    }
    Node*cur = head;
    printf("%d",cur->data);
    while(cur->next!=NULL)
    {
        cur = cur->next;
        printf("%d ",cur->data);
    }
}   

int main(){
    int n;
    scanf("%d",&n);
    int arr[n];

    for(int i = n-1; i >= 0; i--)
    {
        scanf("%d",&arr[i]);
      //  printf("插入成功");

    }

    Node *head = NULL;
    for (int i = 0; i < n; i++)
    {
        AddAtEnd(&head,arr[i]);
    }
    char commad[10] = "0000000000";
    int a,b;
     while(~scanf("%s",commad))
     {
         if(!strcmp(commad,"get"))
         {
            scanf("%d",&a);
            GetNode(head,a);
        }


        if(!strcmp(commad,"delete"))
         {
            scanf("%d",&a);
            DeleteNode(&head,a);
        }



        if(!strcmp(commad,"insert"))
         {
            scanf("%d%d",&a,&b);
            InsertNode(&head,a,b);
        }



        if(!strcmp(commad,"show"))
         {
            
            showNode(head);
        }

    }
    
    return 0;
}

静态链表

本质上就是个结构体数组,每个结构体存有数据域和一个int 索引,把每个节点(结构体)存在数组的固定位置,next就是数组的索引,不用指针了

posted @   __Zed  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示