[일일 쉬운 질문] 데이터 구조 연결 목록 - 단일 연결 목록 oj 질문(1), 몇 가지 일반적인 예를 통해 단일 연결 목록을 빠르게 마스터할 수 있습니다.

여기에 이미지 설명 삽입

Junxi_의 개인 홈페이지

부지런하고 아무도 기다리지 않는 세월을 격려하십시오

C/C++ 게임 개발

안녕하세요 미나상님 Junxi_입니다 오늘은 오랫동안 파묻혀있던 구멍을 메우러 왔어요 여름방학 전에 미리미리 본 시리즈지만 여러 사정으로(주로 제가 키가 작아서) 게으른) 오늘에서야 구멍을 열었습니다. 우리 시리즈는 주로 블로거 자신의 프로그래밍 학습 경험을 기반으로 모듈을 나누어 일부 고전적인 질문 유형을 닦고 질문을 수행하여 콘텐츠의 이 부분에 대한 모든 사람의 숙달을 심화합니다.
자, 더 이상 고민하지 않고 오늘 공부를 시작하겠습니다!

  • 양치 질문의 순서는 입문부터 입문까지이며, 앞 입문의 내용 중 일부는 나중에 입문의 내용과 연관되어 있어 직접 입문에 도움이 될 수 있습니다! ! ! (네, 농담이 아닙니다. 이러한 질문에 대해 신중하게 생각하는 한 독립적으로 완료할 수 있습니다)

1. 연결된 목록 요소 제거

  • 이 질문의 oj 링크는 다음과 같습니다. 연결된 목록 요소를 제거하십시오.
    여기에 이미지 설명 삽입
  • 토픽에 대한 텍스트 설명은 이해하기 어렵지 않은데, 주어진 예제를 조합해보면 이 토픽이 연결 리스트에서 val과 같은 모든 값을 삭제하고 나머지 노드를 연결하는 헤드 노드를 반환하고자 한다는 것을 알 수 있습니다.
  • 다음은 문제 및 코드 구현에 대한 솔루션을 제공합니다.
  • 1. 이 질문은 이전에 이야기한 단일 연결 목록의 중간 삭제와 실제로 매우 유사합니다. 중간 삭제는 주소를 통해 삭제할 위치를 찾는 것이며 이 질문은 val과 같은 값을 찾아야 합니다. 삭제할 노드에서 우리는 val과 같은 노드의 이전 노드가 val과 같은 값을 가진 노드를 건너뛰고 val의 다음 노드를 가리키도록 하고 마지막으로 val과 같은 노드를 해제하기만 하면 됩니다.
struct ListNode* removeElements(struct ListNode* head, int val) {
    
    
    if(head == NULL)//链表中什么都没有
        return NULL;
    
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    
    while(cur)
    {
    
    
        //如果当前节点是需要删除的节点
        if(cur->val == val)
        {
    
    
            //首先保存下一个节点
            struct ListNode* next = cur->next;
            //如果删除的为头节点,更新头节点
            //否则让当前节点的前趋节点链接next节点
            if(prev == NULL)
            {
    
    
                head = cur->next;
            }
            else
            {
    
    
                prev->next = cur->next;  
            }
            //释放当前节点,让cur指向next
            free(cur);
            cur = next;
        }
        else
        {
    
    
            //如果cur不是需要删除的节点,则更新prev,cur
            prev = cur;
            cur = cur->next;
        }
    }
    
    return head;//返回新头
}

여기에 이미지 설명 삽입


2. 역 연결 목록

  • 이 질문 oj에 대한 링크는 다음과 같습니다. Reversing the linked list
    여기에 이미지 설명 삽입

  • 이 질문에 대해 생각하는 방법은 두 가지가 있는데 따로 소개하고 코드로 구현해 보자.

  • 1. Linked List에 Head Insertion이라는 삽입 방법이 있는데 이름에서 알 수 있듯이 Head에 Node를 삽입하는 것이다. 실제로 연결된 목록은 실제로 헤드에서 차례로 삽입됩니다.즉, 5 4 3 2 1입니다. 이것은 우리가 이 질문에서 달성하고자 하는 역방향 연결 목록과 일치하지 않습니까?

struct ListNode* reverseList(struct ListNode* head){
    
    
    //把原链表中的节点依次头插入新链表中
    struct ListNode*newnode=NULL;
    struct ListNode*cur1=head;
    while(cur1)
    {
    
    
        //头插 
      struct ListNode*next=cur1->next;
           
            cur1->next=newnode;
            newnode=cur1;
            cur1=next;
        
    }
    return newnode;
   
}

여기에 이미지 설명 삽입

  • 2. 3개의 포인터를 통해 연결 리스트를 반전시킨다. 이때 포인터가 3개 있다고 가정하고 그 중 n1은 선두를 가리키고, n2는 n1의 다음을 가리키고, n3은 n2의 다음을 가리킨다. 논리 다이어그램이 그림에 나와 있습니다.
    여기에 이미지 설명 삽입
  • 우리는 연결 리스트를 뒤집고자 합니다.두 노드를 연결하는 화살표를 돌릴 수 있습니다.C 언어의 말로는 노드의 포인터가 다음 노드를 가리키는 것에서 이전 노드를 가리키는 것으로 변경하는 것입니다.1에서 1부터 이 시간이 변경됩니다. 새 꼬리의 경우 그림` 과 같이 다음 1을 비웁니다.
    여기에 이미지 설명 삽입
  • 이 단계를 완료하면 연결된 목록의 반전되지 않은 노드도 반전되도록 세 개의 포인터를 한 단계 앞으로 이동해야 합니다.

여기에 이미지 설명 삽입

  • 우리의 끝 조건은 무엇입니까? 우리의 n1이 마지막 노드라는 것은 연결 리스트의 모든 노드가 역전되었다는 뜻인데, 이쯤에서 멈춰도 될까요? 이때 n2가 앞자리이기 때문에, 즉 n2가 비었을 때 n1이 우리의 마지막 자리에 왔기 때문에 n2가 비어있는 것을 루프 정지의 조건으로 사용할 수 있다.

여기에 이미지 설명 삽입

struct ListNode* reverseList(struct ListNode* head) {
    
    
    if(head == NULL || head->next == NULL)//说明此时只有一个节点或者没有节点,直接返回head即可
        return head;
    
    struct ListNode* n1, *n2, *n3;
    n1 = head;
    n2 = n1->next;
    n3 = n2->next;
    n1->next = NULL;
    //中间节点不为空,继续修改指向
    while(n2)
    {
    
    
        //中间节点指向反转
        n2->next = n1;
        //更新三个连续的节点
        n1 = n2;
        n2 = n3;
        if(n3)//防止n3越界,判断一下n3是否为空
            n3 = n3->next;
    }
    //返回新的头
    return n1;
}

여기에 이미지 설명 삽입

3. 연결 리스트의 중간 노드

  • oj 링크는 다음과 같습니다: 링크드 리스트의 중간 노드
    여기에 이미지 설명 삽입
  • 이 oj 질문에 대한 솔루션도 두 가지가 있습니다. 하나씩 분석해 보겠습니다.
  • 1. 폭력적 해결 방법
  • 제목에 필요한 것은 연결된 목록의 중간 노드를 찾는 것입니다. 그런 다음 연결 목록을 순회하면서 동시에 계산할 수 있으므로 연결 목록에 몇 개의 노드가 있는지 기록하는 목표를 달성한 다음 우리는 카운트의 절반을 취하고 연결된 목록이 절반을 통과하도록 합니다. 이때 노드는 우리의 중간 노드입니다!
struct ListNode* middleNode(struct ListNode* head){
    
    
     
        struct ListNode*cur=head;
        struct ListNode*midcur=head;
        int size=0;//标记计数
        while(cur)
        {
    
    
            size++;
            cur=cur->next;
        }
        int len=size/2;//取链表节点数的一半找到中间结点
        while(len)
        {
    
    
            midcur=midcur->next;
            len--;
        }
        return midcur;
}

여기에 이미지 설명 삽입

  • 두 번째 방법은 더 독창적입니다 이해를 돕기 위해 실용적인 예를 들어 보겠습니다.
  • 어느 날 늙은 스님이 젊은 스님에게 몇 가지 질문을 던지고 향 반 개 이내로 대답해 달라고 하였는데 이때 노스님은 시간을 책임지라고 하셨지만 향이 부족하여 절에 향을 꽂아야 할 향이 딱 하나인데 이때 어떻게 해야 할까요?
  • 대답은: 양쪽 끝에서 동시에 굽기 시작하는 것입니다.
  • 이 실용적인 예가 당신에게 영감을 줍니까? 우리는 또한 연결 리스트의 중간 노드인 절반을 요청해야 합니다.
  • 정답 공개: 빠른 포인터와 느린 포인터를 사용합니다. 빠른 포인터는 한 번에 두 단계, 느린 포인터는 한 번에 한 단계, 빠른 포인터가 끝에 도달하면 이때 느린 포인터는 중간 노드에 있습니다. .
  • 类比一下,此时快指针就是从两头烧,慢指针是从一头烧,当两头烧完时,就相当于一头烧烧完了半炷香!!
struct ListNode* middleNode(struct ListNode* head){
    
    
     
       struct ListNode*fast,*slow;
        fast=slow=head;
        while(fast&&fast->next)
        {
    
    
            fast=fast->next->next;
            slow=slow->next;
        }
        return slow;
}

여기에 이미지 설명 삽입


요약하다

  • 오늘 내용은 이것으로 마치겠습니다 한 번에 말을 너무 많이 하면 너무 욕심이 날 수 있으니 이 대표적인 질문 3가지를 당분간 소개하고 추후 다른 관련 내용도 계속 업데이트 하겠습니다. 정말 잘 배우고 싶다면 직접 시도해야 하며 직접 작성하지 않으면 절대 배울 수 없다는 점을 기억하세요! !

  • 그럼 궁금하신 점은 댓글이나 비밀댓글로 물어봐주세요 다음에 또 만나요!

초보 블로거가 만들기 쉽지 않으니 글의 내용이 도움이 되셨다면 이 초보 블로거를 클릭하셔서 떠나시는 것도 좋을 것 같습니다. 귀하의 지원은 업데이트에 대한 동기 부여입니다! ! !

**(케리 블로거 3연속 응원 부탁드립니다!!! 아래 댓글을 눌러 좋아요를 누르고 케리를 도와주세요)**

여기에 이미지 설명 삽입

추천

출처blog.csdn.net/syf666250/article/details/132098918