목차
1.
2. 단방향 비순환 연결리스트 구현
2.1 구현 전 합의" >2.1 구현 전 합의
2.2 addFirst 메소드 " >2.2 addFirst 메소드
2.3 addList 메소드" >2.3 addList 메소드
2.4 addIndex 메소드" >2.4 addIndex 메소드
2.5에는 메소드가 포함되어 있습니다" >2.5에는 메소드가 포함되어 있습니다
2.6 제거 방법" >2.6 제거 방법
2.7 RemoveAllKey 방식 " >2.7 RemoveAllKey 방식
2.8 사이즈와 클리어 방식" >2.8 사이즈와 클리어 방식
3. 단일 연결 리스트 OJ 질문의 심층 분석
3.1 연결된 목록 요소 제거(출처: LeetCode 난이도: 쉬움)" >3.1 연결된 목록 요소 제거(출처: LeetCode 난이도: 쉬움)
3.2 反转链表(来源:LeetCode 难度:简单)" >3.2 反转链表(来源:LeetCode 难度:简单)
3.4 链表中倒数第k个节点" >3.4 链表中倒数第k个节点
3.6 链表分割(来源:牛客网 难度:较难) " >3.6 链表分割(来源:牛客网 难度:较难) 
3.7 链表的回文结构(来源:LeetCode 难度:较难) " >3.7 链表的回文结构(来源:LeetCode 难度:较难) 
3.8 相交链表(来源:LeetCode 难度:简单) " >3.8 相交链表(来源:LeetCode 难度:简单) 
Java java지도 시간 Java 데이터 구조 단일 연결 목록 및 OJ 질문

Java 데이터 구조 단일 연결 목록 및 OJ 질문

Nov 24, 2022 pm 05:38 PM
java

이 글은 java에 대한 관련 지식을 주로 제공하며, 단일 연결 목록, OJ 질문 등 데이터 구조에 관한 내용을 중심으로 모두에게 도움이 되기를 바랍니다.

Java 데이터 구조 단일 연결 목록 및 OJ 질문

추천 학습: "java 비디오 튜토리얼"

1.

연결된 목록은 연결 목록의 참조 링크 순서를 통해 데이터 요소의 논리적 순서가 달성되는 비연속적인 물리적 저장 구조입니다.

일반적인 용어로 각 요소는 노드이며 포인터 필드는 후속 노드를 연결하는 데 사용됩니다. 첫 번째 노드에는 선행 노드가 없고 마지막 노드에는 후속 노드가 없습니다.

실제로 구현되는 연결리스트의 구조는 다음과 같은 상황과 조합하면 매우 다양합니다.

1. , notleading 3. 순환, 비순환

단방향 비순환 연결 목록과 양방향 비순환 연결 목록을 중점적으로 설명합니다. 동시에 이 두 가지도 필기 시험에서 더 자주 테스트됩니다.

2. 단방향 비순환 연결리스트 구현

2.1 구현 전 합의

연결리스트의 각 요소는 노드이므로 내부 클래스 방식을 채택하고 정의도 필요합니다. 헤드 노드 참조는 항상 헤드 노드를 가리킵니다.

public class MySingleList {
    private ListNode head; //引用头节点
    // 链表每个元素是一个节点
    private class ListNode {
        private int val; //存放数据元素
        private ListNode next; //存放下一个节点地址
        //构造方法
        public ListNode(int val) {
            this.val = val;
        }
    }
}
로그인 후 복사

참고: 연결된 목록에는 데이터 필드와 포인터 필드라는 두 개 이상의 필드가 있습니다. 물론 여러 데이터 필드와 포인터 필드를 가질 수도 있습니다.

다음과 같은 일반적인 메소드도 구현해야 합니다.

public void addFirst(int data); //头插法

public void addLast(int data); //尾插法

public boolean addIndex(int index,int data); //任意位置插入,第一个数据节点为0号下标

public boolean contains(int key); //查找关键字key是否在单链表当中

public void remove(int key); //删除第一次出现关键字为key的节点

public void removeAllKey(int key); //删除所有值为key的节点

public int size(); //得到单链表的长度

public void clear(); //清空链表
로그인 후 복사

2.2 addFirst 메소드

public void addFirst(int data) {
    ListNode newNode = new ListNode(data); //把传过来的值放到新的节点中
    newNode.next = this.head; //新节点的next指向头节点
    this.head = newNode; //使新节点成为头节点
}
로그인 후 복사

헤드는 기본적으로 비어 있음을 가리키므로 연결된 목록이 null이면 이 코드의 실행에 영향을 미치지 않습니다. 내 말을 믿지 못하시겠다면 와서 그림을 그려 보세요.

2.3 addList 메소드

public void addLast(int data) {
    ListNode newNode = new ListNode(data);
    // 如果链表为空的情况
    if (this.head == null) {
        this.head = newNode;
        return;
    }
    // 先找到最后一个节点
    ListNode cur = this.head;
    while (cur.next != null) {
        cur = cur.next;
    }
    cur.next = newNode;
}
로그인 후 복사

2.4 addIndex 메소드

//任意位置插入,第一个数据节点为0号下标
private ListNode findIndexPrevNode(int index) {
    ListNode cur = this.head;
    while (index - 1 != 0) {
        cur = cur.next;
        index--;
    }
    return cur;
}
public boolean addIndex(int index,int data) {
    // 判断index下标的有效性
    if (index < 0 || index > size()) {
        return false;
    }
    // 如果在0下标插入则是头插
    if (index == 0) {
        addFirst(data); //头插
        return true;
    }
    // 如果在末尾插入则是尾插
    if (index == size()) {
        addLast(data); //尾插
        return true;
    }

    ListNode newNode = new ListNode(data); //新节点
    // 在中间插入
    ListNode prevNode = findIndexPrevNode(index); //找到index下标的前一个节点
    newNode.next = prevNode.next; //新节点的next被改为index的位置的节点
    prevNode.next = newNode; //index位置前一个节点next被改成新节点
    return true;
}
로그인 후 복사

이 코드에서는 먼저 인덱스 첨자의 이전 노드를 찾고, 먼저 새 노드를 인덱스 위치의 노드에 바인딩해야 합니다. 인덱스의 이전 노드를 바인딩합니다. 노드가 새 노드에 연결되어 있습니다. 여기의 텍스트는 명확하지 않습니다. 친구가 내려와서 내 코드에 따라 그림을 그려서 C 언어 구현에 대한 블로거의 이전 칼럼을 직접 읽을 수도 있습니다. 데이터 구조의 그림이 있습니다.

2.5에는 메소드가 포함되어 있습니다

//查找关键字key是否在单链表当中
public boolean contains(int key) {
    // 当链表为空的情况
    if (this.head == null) {
        return false;
    }
    ListNode cur = this.head;
    // 遍历列表
    while (cur != null) {
        if (cur.val == key) {
            return true; //找到了返回true
        }
        cur = cur.next;
    }
    return false; //找不到返回false
}
로그인 후 복사

아이디어는 매우 간단합니다. 연결된 목록을 탐색하여 cur의 빈 위치를 찾으면 true를 반환하고, false를 반환하지 않으면 친구에게 그림을 그려주세요.

2.6 제거 방법

//删除第一次出现关键字为key的节点
public void remove(int key) {
    if (this.head == null) {
        return;
    }
    ListNode cur = this.head;

    // 如果删除的是key为头节点
    if (this.head.val == key) {
        this.head = head.next;
        return;
    }

    // 这里不能是cur!=null, 不然会越界!!!
    while (cur.next != null) {
        // 找到 key 的前一个节点
        if (cur.next.val == key) {
            //当前的cur为key的前一个节点
            cur.next = cur.next.next; //cur链接到key的后一个节点
            return;
        }
        cur = cur.next;
    }
}
로그인 후 복사

여기서 키의 이전 노드를 찾은 다음 키 뒤에 있는 노드에 바인딩해야 합니다. 키에 해당하는 노드가 더 이상 참조되지 않으면 자동으로 재활용됩니다. .

2.7 RemoveAllKey 방식

//删除所有值为key的节点
public void removeAllKey(int key) {
    if (this.head == null) {
        return;
    }
    // 采用前后指针的方法
    ListNode cur = this.head;
    ListNode prev = this.head;
    while (cur != null) {
        if (cur.val == key) {
            prev.next = cur.next; //跳过key节点指向下一个节点
        } else {
            prev = cur;
        }
        cur = cur.next;
    }
    // 判断第一个节点是不是key
    if (this.head.val == key) {
        this.head = this.head.next; //head指向下一个节点
    }
}
로그인 후 복사

먼저 직접 살펴보겠습니다. 이 질문은 나중에 OJ 질문을 설명할 때 자세히 설명하겠습니다.

2.8 사이즈와 클리어 방식

이 두 가지 방식은 뭐 말이 필요 없을 것 같죠? 횡단? 헤드 포인터를 null로 직접 설정하시겠습니까? 이건 간단하지 않나요? 여기에는 쓰지 않겠습니다.

3. 단일 연결 리스트 OJ 질문의 심층 분석

이게 오늘의 하이라이트입니다. 농구 선수가 그림을 안 그리는 것이 아니라, 이전 그림이 너무 단순해서입니다. 하지만 OJ 질문의 경우 여전히 그림을 그려야 합니다. 이 질문을 읽고 나면 데이터 구조에 빠지게 될 것이라고 믿습니다.

3.1 연결된 목록 요소 제거(출처: LeetCode 난이도: 쉬움)

질문: 연결된 목록의 헤드 노드head 和一个整数 val ,请你删除链表中所有满足 Node.val == val를 제공하고 새 헤드 노드를 반환합니다.

这个题我们可以用前后指针的思路来做,这样也比较通俗易懂,更适合初学者,大概的思路是这样的:我们可以定义一个cur和first的引用,如果碰到相等,也就是first.val == val,我们则让cur的next指向first的下一个节点,如果不相等,则让cur走到first的位置,最后first往后走一步,图解:

这里还没有完,如果第一个节点的值也是val呢?所以最后我们别忘了进行一个判断,那么最终的代码是这样的:

public ListNode removeElements(ListNode head, int val) {
    if (head == null) {
        return null;
    }
    ListNode cur = head;
    ListNode first = head;
    while (first != null) {
        if (first.val == val) {
            cur.next = first.next;
        } else {
            cur = first;
        }
        first = first.next;
    }
    // 判断头节点的值是否也是val
    if (head.val == val) {
        head = head.next;
    }
    return head;
}
로그인 후 복사

3.2 反转链表(来源:LeetCode 难度:简单)

题目:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 

这个题我们可以先取到头节点,后续的节点都进行头插法即可?我们取到头节点,并且先将头节点的next置空,但是这样一来,就找不到后面的节点了,所以我们还需要有一个curNext引用来记录要反转节点的下一个节点:

我们的思路是这样的:首先找到头节点的next置空,cur走到curNext位置,curNext往前走,使得cur位置的next指向头节点,头节点cur再次成为新的头节点,当curNext走到null的时候循环结束。

public ListNode reverseList(ListNode head) {
    // 空链表的情况
    if (head == null) {
        return null;
    }
    ListNode cur = head;
    ListNode curNext = cur.next;
    head.next = null;
    while (curNext != null) {
        cur = curNext;
        curNext = curNext.next;
        cur.next = head;
        head = cur;
    }
    return head;
}
로그인 후 복사

3.4 链表中倒数第k个节点

题目:输入一个链表,输出该链表中倒数第k个结点。 

这个题也是很简单的一道题,可以采用前后指针法,先让first指针走k步,走完之后slow和first一起走,这样slow和first之间就相差了k步,当first为null时,slow就是倒数第k个节点,在这个过程中,我们还要判断k的合法性,如果k小于等于0?或者k大于链表的长度呢?于是我们就能写出如下的代码:

public ListNode FindKthToTail(ListNode head,int k) {
    // 判断k的合法性
    if (k <= 0 || head == null) {
        return null;
    }
    ListNode first = head;
    ListNode slow = head;
    // 先让first走k步
    while (k != 0) {
        // k的长度大于链表的长度
        if (first == null) {
            return null;
        }
        first = first.next;
        k--;
    }
    // 一起走,当first为null,slow就是倒数第k个节点
    while (first != null) {
        first = first.next;
        slow = slow.next;
    }
    return slow;
}
로그인 후 복사

3.6 链表分割(来源:牛客网 难度:较难) 

题目:现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。 

这个题的思路我们可以这样做,既然是按照给定的值x进行分割,那么我们设定两个盘子,盘子A放小于x的节点,盘子B放大于x的节点,最后把这两个盘子的节点连起来,放回盘子A的头节点即可!

 public ListNode partition(ListNode pHead, int x) {
        if (pHead == null) {
            return null;
        }
        ListNode headA = null;
        ListNode headB = null;
        ListNode curA = null;
        ListNode curB = null;
        ListNode cur = pHead;
        while (cur != null) {
            if (cur.val < x) {
                // 第一次放入A盘子
                if (headA == null) {
                    headA = cur;
                    curA = cur;
                } else {
                    curA.next = cur;
                    curA = cur;
                }
            } else {
                // 第一次放入B盘子
                if (headB == null) {
                    headB = cur;
                    curB = cur;
                } else {
                    curB.next = cur;
                    curB = cur;
                }
            }
            cur = cur.next;
        }
        // 如果A盘子为空
        if (headA == null) {
            return headB;
        }
        curA.next = headB;
        // 避免B盘子尾节点形成环
        if (headB != null) {
            curB.next = null;
        }
        return headA;
    }
로그인 후 복사

3.7 链表的回文结构(来源:LeetCode 难度:较难) 

题目:对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

这个题有要求的,要求空间复杂度为O(1),并且还得在O(n)的时间复杂度下,那我们就原地解决这个题,我们可以分为三个步骤,首先找到中间节点,然后把中间节点往后的节点进行反转,最后左右两个指针同时往中间走。如果光看下面代码看不懂,可以结合着代码画图才能理解更透彻哦!

public boolean chkPalindrome(ListNode A) {
    if (A == null) {
        return false;
    }
    // 只有一个节点的情况
    if (A.next == null) {
        return true;
    }
    // 首先找到中间节点
    ListNode first = A;
    ListNode slow = A;
    while (first != null && first.next != null) {
        first = first.next.next;
        slow = slow.next;
    }
    // 走到这,slow是链表的中间节点,采用头插法反转slow后续的节点
    first = slow.next;
    ListNode cur = slow;
    while (first != null) {
        cur = first;
        first = first.next;
        cur.next = slow; //链接前一个节点
        slow = cur; //更新头节点的位置
    }
    // 走到这,反转完毕,cur指向最后一个节点,让slow等于A,往中间找
    slow = A;
    while (slow != cur) {
        if (slow.val != cur.val) {
            return false;
        }
        // 偶数的情况下需要特殊判断
        if (slow.next == cur) {
            return true;
        }
        slow = slow.next;
        cur = cur.next;
    }
    return true;
}
로그인 후 복사

3.8 相交链表(来源:LeetCode 难度:简单) 

题目:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

这个题我们可以这样做,长链表先走两个链表的长度差的步数,因为相交之后的节点都是一样的个数,所以走了差值后,就两个链表一起往后走,相等了则就是相交节点。

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    if (headA == null || headB == null) {
        return null;
    }
    ListNode longList = headA; //longList始终记录长的链表
    ListNode shortList = headB;
    // 分别求出两个链表的长度
    int lenA = 0;
    int lenB = 0;
    ListNode cur = headA;
    while (cur != null) {
        lenA++;
        cur = cur.next;
    }
    cur = headB;
    while (cur != null) {
        lenB++;
        cur = cur.next;
    }
    int len = lenA - lenB;
    // 如果B链表长于A链表
    if (len < 0) {
        // 修正相差长度
        len = lenB - lenA;
        longList = headB; //longList始终记录长的链表
        shortList = headA;
    }
    // 让长链表先走差值len步,然后同步走,相等了即为相交节点
    while (len != 0) {
        longList = longList.next;
        len--;
    }
    while (longList != shortList) {
        longList = longList.next;
        shortList = shortList.next;
    }
    // 如果两个链表走到了null,则没有中间节点返回null,如果有,返回任意一个即可
    return longList;
}
로그인 후 복사

推荐学习:《java视频教程

위 내용은 Java 데이터 구조 단일 연결 목록 및 OJ 질문의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

자바의 완전수 자바의 완전수 Aug 30, 2024 pm 04:28 PM

Java의 완전수 가이드. 여기서는 정의, Java에서 완전 숫자를 확인하는 방법, 코드 구현 예제에 대해 논의합니다.

자바의 웨카 자바의 웨카 Aug 30, 2024 pm 04:28 PM

Java의 Weka 가이드. 여기에서는 소개, weka java 사용 방법, 플랫폼 유형 및 장점을 예제와 함께 설명합니다.

Java의 스미스 번호 Java의 스미스 번호 Aug 30, 2024 pm 04:28 PM

Java의 Smith Number 가이드. 여기서는 정의, Java에서 스미스 번호를 확인하는 방법에 대해 논의합니다. 코드 구현의 예.

Java Spring 인터뷰 질문 Java Spring 인터뷰 질문 Aug 30, 2024 pm 04:29 PM

이 기사에서는 가장 많이 묻는 Java Spring 면접 질문과 자세한 답변을 보관했습니다. 그래야 면접에 합격할 수 있습니다.

Java 8 Stream foreach에서 나누거나 돌아 오시겠습니까? Java 8 Stream foreach에서 나누거나 돌아 오시겠습니까? Feb 07, 2025 pm 12:09 PM

Java 8은 스트림 API를 소개하여 데이터 컬렉션을 처리하는 강력하고 표현적인 방법을 제공합니다. 그러나 스트림을 사용할 때 일반적인 질문은 다음과 같은 것입니다. 기존 루프는 조기 중단 또는 반환을 허용하지만 스트림의 Foreach 메소드는이 방법을 직접 지원하지 않습니다. 이 기사는 이유를 설명하고 스트림 처리 시스템에서 조기 종료를 구현하기위한 대체 방법을 탐색합니다. 추가 읽기 : Java Stream API 개선 스트림 foreach를 이해하십시오 Foreach 메소드는 스트림의 각 요소에서 하나의 작업을 수행하는 터미널 작동입니다. 디자인 의도입니다

Java의 날짜까지의 타임스탬프 Java의 날짜까지의 타임스탬프 Aug 30, 2024 pm 04:28 PM

Java의 TimeStamp to Date 안내. 여기서는 소개와 예제와 함께 Java에서 타임스탬프를 날짜로 변환하는 방법에 대해서도 설명합니다.

캡슐의 양을 찾기위한 Java 프로그램 캡슐의 양을 찾기위한 Java 프로그램 Feb 07, 2025 am 11:37 AM

캡슐은 3 차원 기하학적 그림이며, 양쪽 끝에 실린더와 반구로 구성됩니다. 캡슐의 부피는 실린더의 부피와 양쪽 끝에 반구의 부피를 첨가하여 계산할 수 있습니다. 이 튜토리얼은 다른 방법을 사용하여 Java에서 주어진 캡슐의 부피를 계산하는 방법에 대해 논의합니다. 캡슐 볼륨 공식 캡슐 볼륨에 대한 공식은 다음과 같습니다. 캡슐 부피 = 원통형 볼륨 2 반구 볼륨 안에, R : 반구의 반경. H : 실린더의 높이 (반구 제외). 예 1 입력하다 반경 = 5 단위 높이 = 10 단위 산출 볼륨 = 1570.8 입방 단위 설명하다 공식을 사용하여 볼륨 계산 : 부피 = π × r2 × h (4

미래를 창조하세요: 완전 초보자를 위한 Java 프로그래밍 미래를 창조하세요: 완전 초보자를 위한 Java 프로그래밍 Oct 13, 2024 pm 01:32 PM

Java는 초보자와 숙련된 개발자 모두가 배울 수 있는 인기 있는 프로그래밍 언어입니다. 이 튜토리얼은 기본 개념부터 시작하여 고급 주제를 통해 진행됩니다. Java Development Kit를 설치한 후 간단한 "Hello, World!" 프로그램을 작성하여 프로그래밍을 연습할 수 있습니다. 코드를 이해한 후 명령 프롬프트를 사용하여 프로그램을 컴파일하고 실행하면 "Hello, World!"가 콘솔에 출력됩니다. Java를 배우면 프로그래밍 여정이 시작되고, 숙달이 깊어짐에 따라 더 복잡한 애플리케이션을 만들 수 있습니다.

See all articles