LoginSignup
2
2

More than 5 years have passed since last update.

アルゴリズム1000本ノック #5. Find N-th element from tail of a linked list

Last updated at Posted at 2015-12-18

問題

与えられた単方向リストの最後からN番目の要素を求めよ.
該当の要素が無い場合はNULLを返却すること.

Solution

簡単に思いつく方法としては,

  1. 与えられたリストを最初から最後まで走査して, リストの長さを割り出す
  2. 後ろからN番目の要素は, リストの長さをMとすると先頭から(N - M + 1) 番目なので, 再度前からリストを走査する

があります.

algorithm_linkedlist_find_nth_from_tail_1.py
# Class of linked list Node.
class Node():
    def __init__(self, val):
        self.next = None
        self.value = val

    def next(self):
        return self.next

    def setNext(self, nextNode):
        self.next = nextNode


def findElement(head, N):
    if head == None:
        return None

    curNode = head
    num = 1 # number of elements in list
    while curNode.next:
        num += 1
        curNode = curNode.next

    if num < N or N <= 0: # list is too short
        return None

    # get num - N + 1 node
    curNode = head
    curNum  = 1
    while curNum < num - N + 1:
        curNum += 1
        curNode = curNode.next
    return curNode.value

計算量: O(M), 空間量: O(1)

algorithm_linkedlist_find_nth_from_tail_1.java
// Class of linked list node
class Node {
    public int  value;
    public Node next;

    public Node(int value) {
        this.value = value;
        this.next = null;
    }

    public void setNext(Node nextNode) {
        this.next = nextNode;
    }
}

class Solution
{
    public static Node findNthNodeFromTail(Node head, int N) {
        if (head == null) return null;

        int length   = 1;
        Node curNode = head;
        while (curNode.next != null) {
            length++;
            curNode = curNode.next;
        } // now length = length of the given list

        if (length <= 0 || length < N) return null;

        int curLength = 1;
        curNode       = head;
        while (curLength < length - N + 1) {
            curNode = curNode.next;
            curLength++;
        }
        return curNode;
    }
}

これも有向な方法ですが, リストを二回走査しています.
これを一回に減らそうと思うと, リストのノードを指すポインタを2つ用意して

  1. まず1つのポインタを先頭からN番目の要素まで進める
  2. もう1つのポインタを先頭に当てる
  3. 最初のポインタがtailに到達するまで, 両方のポインタを1つずつ進めていく

こうすると, 最後の状態として2つ目のポインタが後ろからN番目の要素で止まっていることになります.

algorithm_linkedlist_find_nth_from_tail_2.py
# Class of linked list Node.
def findElement(head, N):
    if head == None:
        return None

    p1 = head
    num = 1
    while num < N:
        num += 1
        p1 = p1.next
        if p1 is None:
            return None

    # get num - N + 1 node
    p2 = head
    while not p1.next is None:
        p1 = p1.next
        p2 = p2.next
    return p2

計算量: O(M), 空間量: O(1)

algorithm_linkedlist_find_nth_from_tail_2.java
class Solution
{
    public static Node findNthNodeFromTail(Node head, int N) {
        if (head == null) return null;

        int length   = 1;
        Node p1 = head;
        while (length < N) {
            length++;
            p1 = p1.next;

            if (p1 == null) return null;
        } // now p1 is on the Nth node from the head

        Node p2 = head;
        while (p1.next != null) {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p2;
    }
}

for分の繰り返しの数を考えると, リスト1巡り分しか走査していないことに注目です。ただし, 計算量のオーダーで考えると2MもMもO(M)なので, 2つの方法に差はありません。

このように, Linked listの問題では, 複数のポインタを用意して異なるペース, あるいは異なる出発点で各々を走査させることで, 問題を効率的に解くことが可能になることがあります.

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2