[Questions de test de surface de lien] Résolvez le problème de la liste liée circulaire et de la liste liée qui se croise

J'ai trouvé plusieurs questions comme celle-ci sur Likou, j'ai essayé de le faire, et j'ai trouvé un moyen de faire ce genre de question : l'utilisation des pointeurs de vitesse. Pas beaucoup de bêtises, le dernier exemple

Table des matières

1. Anneau liste liée

1. Définition (notion)

2. Comment juger s'il s'agit d'une liste chaînée circulaire

1. Pointeur rapide et lent

2. Pourquoi le pointeur rapide parcourt-il deux nœuds à la fois ?

3. Exemple d'analyse

Deux listes chaînées qui se croisent

1. Exemple d'analyse

2. Analyse des solutions

Résumer


1. Anneau liste liée

1. Définition (notion)

La soi-disant liste chaînée circulaire n'est rien de plus que celle-ci, comme le montre la figure

 Une telle liste chaînée peut être appelée une liste chaînée circulaire.

2. Comment juger s'il s'agit d'une liste chaînée circulaire

Étant donné une liste chaînée, comment jugeons-nous s'il s'agit d'une liste chaînée circulaire ?

Ensuite, apprenons à connaître le concept de pointeurs rapides et lents

1. Pointeur rapide et lent

Les pointeurs rapides et lents, c'est-à-dire que le pointeur lent effectue une étape à la fois et que le pointeur rapide effectue deux étapes à la fois. Les deux pointeurs commencent à courir à partir de la position de départ de la liste liée. Si la liste liée a un anneau, ils se rencontreront certainement dans l'anneau, sinon le pointeur rapide ira d'abord à la fin de la liste liée. Par exemple : Accompagnez votre petite amie pour courir et perdre du poids.

C'est-à-dire pour prendre la tête de la table comme position initiale, on définit deux nœuds rapide et lent, pointant vers le nœud de tête, rapide à chaque fois, aller deux nœuds, ralentir à chaque fois un nœud, s'il y a un anneau, il va rencontre inévitable (rapide ==lent), attention, les adresses des pointeurs se valent ! S'il n'y a pas de boucle, il ira plus vite à la fin de la liste chaînée et pointera vers NULL

2. Pourquoi le pointeur rapide parcourt-il deux nœuds à la fois ?

En supposant une montre liée avec un anneau, les deux aiguilles finiront par entrer dans l'anneau. L'aiguille rapide entre d'abord dans l'anneau, et l'aiguille lente entre dans l'anneau plus tard. Lorsque l'aiguille lente entre juste dans l'anneau, elle peut rencontrer l'aiguille rapide. le pire des cas est la distance La longueur de l'anneau. À ce moment, chaque fois que les deux pointeurs se déplacent, la distance entre eux sera réduite d'un pas et il n'y aura pas de situation où il se trouve qu'il s'agit d'une boucle (impossible de se rencontrer à chaque fois). Par conséquent, avant que le pointeur lent ne fasse un cercle, le pointeur rapide pourra certainement rattraper Celui avec le pointeur lent, c'est-à-dire rencontrer!

comme le montre l'image:

 comme le montre l'image:

3. Exemple d'analyse

Leetcode icon-default.png?t=N0U7https://leetcode.cn/problems/linked-list-cycle/Ceci est un lien vers un exemple de problème, une liste circulaire liée

Les sujets sont les suivants :

Les codes AC sont les suivants :

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode *fast=head;
    struct ListNode *slow=head;
 
    // if(fast!=NULL||fast->next!=NULL){
    //     return false;
    // }
    while(fast!=NULL&&fast->next!=NULL){
        slow=slow->next;
        fast=fast->next->next;
        if(fast==slow){
            return true;
        }
    }
    return false;
}

1. Boucle While, parce que head est le nœud principal, head est NULL, indiquant une liste chaînée vide, head->next est NULL, indiquant qu'il n'y a qu'un seul nœud et qu'un anneau ne peut pas être formé.

2. Nous utilisons une boucle while, utilisons des pointeurs rapides et lents, le pas rapide est 2 et le pas lent est 1. Si fast==slow renvoie vrai (avec une boucle), après la fin de la boucle, renvoie faux (pas de boucle )

Il existe aussi une solution plus violente
:

code afficher comme ci-dessous:

    while(head!=NULL){
        if(head->val==100000){
            return true;
        }
        head->val=100000;
        head=head->next;
    }
    return false;

La deuxième question qui peut utiliser le pointeur de vitesse

Le k-ième dernier nœud de la liste chaînée

Comme indiqué sur l'image :

L'idée d'utiliser les pointeurs rapides et lents est que si rapide et lent se déplacent en même temps, l'écart entre les pointeurs pointés par rapide et lent sera augmenté de 1. Par conséquent, l'avant-dernier nœud n'a besoin que de être lorsque la distance entre rapide et lent est k , les deux se déplacent avec une taille de pas de 1, rapide, lors du passage à NULL, lent pointe vers l'avant-dernier k-ième nœud

code afficher comme ci-dessous:

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param pListHead ListNode类 
 * @param k int整型 
 * @return ListNode类
 */
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    // // write code here
    // if(pListHead==NULL){
    //     return NULL; //判断是否为NULL
    // }
    // struct ListNode*cur=pListHead;
    // int count=0;
    // while(cur!=NULL){
    //     count++;
    //     cur=cur->next;
    // }
    // //得到了结点个数
    // if(count==0){
    //     return NULL;  //如果空链表,自然返回NULL
    // }
    // int num=count+1-k;//这是num得到倒数第k个结点  count-k+1
    // if(k>count||k<=0){    //如果k大于count,当然是不行的,小于0也不行,都返回NULL
    //     return NULL;
    // }
    // while(--num){     //然后进行循环即可
    //     pListHead=pListHead->next;
    // }
    // return pListHead;

//快慢指针问题,设计两个指针,fast和slow两种,然后先移动fast指针k步,然后一起移动两个指针,直到fast移动到NULL的时候,slow就是倒数第k个结点
    struct ListNode*fast=pListHead;
    struct ListNode*slow=pListHead;
    while(k--){
        if(fast==NULL){
            return NULL;//如果为空链表,那么就返回NULL
        }
        fast=fast->next;
    }
    while(fast){
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}

La boucle while se déplace k fois, puis se déplace simultanément avec une taille de pas de 1 jusqu'à ce que le fast soit NULL. C'est l'idée d'utiliser les pointeurs rapides et lents.

Bien sûr, c'est une pratique courante d'être //, et il est attaché

Deux listes chaînées qui se croisent

Comme indiqué sur l'image :

1. Exemple d'analyse

Leetcode https://leetcode.cn/problems/intersection-of-two-linked-lists/

2. Analyse des solutions

Le premier est représenté sur la figure :

code afficher comme ci-dessous:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA==NULL||headB==NULL){
        return NULL;
    }
   struct ListNode*p=headA;
   struct ListNode*q=headB;
    while(p!=q){
        p=p==NULL?headB:p->next;
        q=q==NULL?headA:q->next;//因为比较的是指针的地址,所以如果指向NULL之后,还可以继续找到头节点
        //1.结点数相同的时候,一次遍历就能得到是否有相交,这个结点就是p指向的位置,如果没有,p正好指向null
        //2.如果结点数不同的时候,那么因为不同,所以当结点数小的那个遍历一边的时候,结点数大的还没完成,这样永远差一个结点数之差的位置,然后继续接上上两个头节点,这样第二轮,就是在尾部对齐的情况下,起始位置相同,这样一轮必然能得到答案
    }
    return p;
}

Analyse de la seconde solution :

Principalement, parcourez d'abord pour obtenir la longueur des deux listes liées, pq pointe vers les nœuds principaux headA et headB de la liste liée respectivement, puis donnez le nœud principal de la liste liée plus longue au nœud p, puis obtenez la différence entre le nombre de nœuds dans les deux listes chaînées, num=lenA- lenB, puis déplacez le nœud p num fois, pendant la boucle, puis déplacez les nœuds p et q en même temps pour déterminer s'il existe un nœud qui se croise

code afficher comme ci-dessous:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA==NULL||headB==NULL){
        return NULL;
    }
//    struct ListNode*p=headA;
//    struct ListNode*q=headB;
//     while(p!=q){
//         p=p==NULL?headB:p->next;
//         q=q==NULL?headA:q->next;//因为比较的是指针的地址,所以如果指向NULL之后,还可以继续找到头节点
//         //1.结点数相同的时候,一次遍历就能得到是否有相交,这个结点就是p指向的位置,如果没有,p正好指向null
//         //2.如果结点数不同的时候,那么因为不同,所以当结点数小的那个遍历一边的时候,结点数大的还没完成,这样永远差一个结点数之差的位置,然后继续接上上两个头节点,这样第二轮,就是在尾部对齐的情况下,起始位置相同,这样一轮必然能得到答案
//     }
//     return p;

//第二种做法
//目的是,我们想让他们尾部对齐,然后在同长度的情况下,遍历,这样就能找到相交结点,或者NULL
    struct ListNode*p=headA;
    struct ListNode*q=headB;
    if(headA==NULL||headB==NULL){
        return NULL;
    }
    //取得第一个A链表的长度
    int lenA=0;
    while(p!=NULL){
        lenA++;
        p=p->next;
    }
    int lenB=0;
    while(q!=NULL){
        lenB++;
        q=q->next;
    }

    //找到之后,将最大的结点数都给A  就是交换位置
    p=headA;
    q=headB;
    if(lenA<lenB){
        //交换结点长度
        int temp=lenA;
        lenA=lenB;
        lenB=temp;
        //交换链表头节点
        struct ListNode*node=p;
        p=q;
        q=node;
    }
    //这样得到的是A为最长结点
    //两个结点相互减去
    int count=lenA-lenB;
    while(count--){
        p=p->next;       
    }
    while(p!=q){
        p=p->next;
        q=q->next;
    }
    return p;
}

Résumer

Après avoir compris le concept de pointeurs rapides et lents, nous pouvons juger la liste liée circulaire, et le problème des listes liées qui se croisent peut également être réalisé. Cependant, il convient de mentionner qu'il existe vraiment de nombreuses solutions intéressantes et intéressantes dans la zone de discussion Lituo .

Par exemple, le classique retour vrai ; (50 % de réponse) est vraiment amusant ! ! !

Je suppose que tu aimes

Origine blog.csdn.net/qq_63319459/article/details/128869226
conseillé
Classement