線形リストの順序リスト実装(Pythonによる記述)

線形リストは2種類の格納形式に分けられます:順序格納と連結格納

順序格納:

順序格納では、線形リストのノードは論理順に連続したアドレスのメモリ領域に格納されます。この方法で格納された線形リストを順序リストと呼びます。2つの特徴があります:1. 論理順序と物理順序が一致している;2. データ要素間の関係はコンピュータ内での「物理的な位置の隣接」によって表現されます。順序リストの各要素のデータ型が同じであると仮定し、n個のメモリ領域が必要で、各要素がL個のメモリ領域を占めるとします。最初の要素が占めるメモリアドレスをデータ要素の開始位置として、順序リストのi+1番目のデータ要素の格納位置Loc(i+1)とi番目のデータ要素の格納位置Loc(i)は以下の関係を満たします:Loc(i+1)=Loc(i)+L。

1. 順序リストの初期化:

def __init__(self, max_size):
    self.max_size = max_size    # 順序リストの最大容量
    self.current_size = 0    
    self.elements = [None for _ in range(self.max_size)]
    
# seq_list = SequentialList(100) のように、100がmax_sizeに渡されます

2. インデックスによる要素の検索:

def get_element(self, index):
    # 指定されたインデックスの要素を取得
    if index < 0 or index >= self.current_size:
       raise IndexError('インデックスが不正です')
    else:
       return self.elements[index]

3. インデックス位置の要素の更新:

def set_element(self, index, value):
   if index < 0 or index >= self.current_size:
       raise IndexError('インデックスが不正です')
   else:
       self.elements[index] = value

4. 順序リストが空かどうかの判定:

def is_empty(self):
    return self.current_size == 0

5. リストの末尾に要素を追加:

def add_element(self, value):
   if self.current_size == self.max_size:
      return False
   else:
      self.elements[self.current_size] = value
      self.current_size += 1
      return True

6. 順序リストの任意の位置に要素を挿入:

'''まずインデックスが有効かどうかを確認し、無効であれば例外を投げます。
有効であれば、インデックス位置およびそれ以降の要素を一つ後ろに移動し、
インデックス位置に要素を追加します。実装は以下の通りです:'''
def insert_element(self, index, value):
    if index < 0 or index > self.current_size:
       raise IndexError('インデックスが不正です')
    if index == self.current_size:  # インデックスがリストの末尾の場合
       self.add_element(value)
    else:
       # 要素を後ろにずらすためのスペースを作成
       self.elements.append(None)
       # 後ろから前に向かって要素を一つずつ後ろに移動
       for i in range(self.current_size, index, -1):
            self.elements[i] = self.elements[i-1]
       # 新しい要素を挿入
       self.elements[index] = value
       self.current_size += 1
    return self.elements

7. リストの末尾要素の削除:

def remove_last(self):
    if self.is_empty():
       return None
    self.current_size -= 1
    # 最後の要素を削除
    last_element = self.elements[self.current_size]
    self.elements = self.elements[:self.current_size]
    return last_element

ヒント:深いコピーと浅いコピー

不変オブジェクト:ほとんどのPython組み込みデータ型:数値、文字列、タプル
ID以外の状態は変更される可能性があります。可変オブジェクト:リスト、セット、辞書

lst = []
print("変更前のID:", id(lst))
 
lst.append(132)
print("変更後のID:", id(lst))
print(lst)

# 出力されるIDは同じです
num = 1
print("変更前のID:", id(num))
 
num = 4
print("変更後のID:", id(num))  # IDが変わっているため、データの変更は元のメモリ上ではなく、
                              # 新しくメモリを確保して新しい値を保存していることを示します
print(num)

# 出力されるIDは異なります

深いコピーと浅いコピー:

original = [1,2,3]
shallow_copy = original
shallow_copy[0] = 8
print(shallow_copy)
'''ここでshallow_copyも[8,2,3]になっていることがわかります。
なぜなら「shallow_copy = original」は浅いコピーであり、新しいメモリ領域を割り当てていないため、
shallow_copyが指すアドレスの内容を変更することと同じだからです'''
import copy
deep_copy = copy.deepcopy(original)
deep_copy[0] = 9
print(deep_copy)
'''
ここでoriginalがdeep_copyの変更によって変わっていないことがわかります。
なぜならdeepcopyは深いコピーであり、deep_copy用に新しいメモリ領域を確保しているからです
'''

8. 任意の位置の要素の削除:

def delete_element(self, index):
   if self.is_empty() or index >= self.current_size:
       raise IndexError('インデックスが不正です')
   # 削除された要素を返す
   deleted_element = self.elements[index]
   # 削除位置以降の要素を前に詰める
   for i in range(index, self.current_size - 1):
       self.elements[i] = self.elements[i+1]
   self.current_size -= 1
   return deleted_element

9. 順序リストの長さの取得:

def get_length(self):
    return self.current_size

10. 順序リストを走査して各要素を表示:

def traverse(self):
    for element in self.elements[:self.current_size]:
        print(element)

順序リストの実装例:

#!/usr/bin/python3
# 線形リストは順序リストと連結リストに分けられます

# 順序リストの実装
class SequentialList:
    def __init__(self):
        self.elements = []

    def check_empty(self):
        if len(self.elements) == 0:
            print('空です')
        else:
            print('空ではありません')

    def create_list(self, size):
        if len(self.elements) == 0:
           for i in range(0, size):
                self.elements.append(i)
        return self.elements

    def get_length(self):
        return len(self.elements)

    def insert_at(self, position, element):
        length = len(self.elements)
        # スペースを確保するために一時的に要素を追加
        self.elements.append(None)
        # 後ろから前に向かって要素を移動
        for j in range(length, position, -1):
            self.elements[j] = self.elements[j-1]
        # 新しい要素を挿入
        self.elements[position] = element
        print(f'位置 {position} に要素 {element} を挿入しました')
        return self.elements

    def delete_at(self, position):
        length = len(self.elements)
        # 削除される要素を保存
        deleted = self.elements[position]
        # 削除位置以降の要素を前に詰める
        for j in range(position, length - 1):
            self.elements[j] = self.elements[j+1]
        # リストの最後の要素を削除
        self.elements.pop()
        return self.elements, deleted

    def create_sample(self):
        self.elements.append('apple')
        self.elements.append('banana')

    def find_element(self, target):
        for i in range(0, len(self.elements)):
            if self.elements[i] == target:
                print(f'見つかりました。インデックス: {i}')


if __name__ == "__main__":  # インポート時に実行されないように保護
    my_list = SequentialList()
    my_list.create_sample()
    print(my_list.insert_at(0, 'pear'))
    my_list.find_element('apple')

タグ: 線形リスト 順序リスト Python データ構造 アルゴリズム

5月15日 05:08 投稿