-
1 # 自媒體錢多多
-
2 # 網際網路晨哥
實現基本的資料結構時指標唯一的作用就是指向,不涉及指標運算(pointer arithmetic)(這也不叫 const pointer),所以 Java / Python 的引用已經足夠做到這一點了。就算實現 B-tree 什麼的,配個數組也夠了。隨手寫個 Python 的連結串列節點,Java 的下面有人貼例子了,都是一樣的道理。需要class Node(object): def __init__(self, value, nextnode=None): self.value = value self._nextnode = nextnode def append(self, n): if not isinstance(n, Node): n = Node(n) self._nextnode, n = n, self._nextnode self._nextnode._nextnode = n標籤都有“演算法與資料結構”了,實現資料結構練習而已,又不是研究 Python 內建物件的實現方法……什麼 PyObject 的就走遠了哈。
-
3 # web前端愛好者
java或者python雖然沒有指標, 但是有引用型別啊, 一樣可以實現資料結構。
單鏈表結構的節點類:
public class Node {
public Object data;
public Node next;
public Node(Object e){
this.data = e;
}
}
雙鏈表結構的節點類:
public class Node {
public Object e;
public Node next;
public Node pre;
public Node(){
}
public Node(Object e){
this.e = e;
next = null;
pre = null;
}
}
-
4 # 我的技術小棧
資料結構跟指標沒什麼關係,一樣可以實現的,題主可以閱讀下java的linkedlist原始碼,估計會有不一樣的感受
-
5 # 網際網路小蝌蚪
JAVA
Java語言中的對bai象引用實際上是一個指標(這裡的指標均為概念上的意義,而非語言提供的資料型別),所以我們可以編寫這樣的類來實現連結串列中的結點。
程式程式碼:
class Node
{
Object data;
Node next;//指向下一個結點
}
將資料域定義成Object類是因為Object類是廣義超類,任何類物件都可以給其賦值,增加了程式碼的通用性。為了使連結串列可以被訪問還需要定義一個表頭,表頭必須包含指向第一個結點的指標和指向當前結點的指標。為了便於在連結串列尾部增加結點,還可以增加一指向連結串列尾部的指標,另外還可以用一個域來表示連結串列的大小,當呼叫者想得到連結串列的大小時,不必遍歷整個連結串列。
連結串列的資料結構我們可以用類List來實現連結串列結構,用變數Head、Tail、Length、Pointer來實現表頭。儲存當前結點的指標時有一定的技巧,Pointer並非儲存指向當前結點的指標,而是儲存指向它的前趨結點的指標,當其值為null時表示當前結點是第一個結點,因為當刪除當前結點後仍需保證剩下的結點構成連結串列,如果Pointer指向當前結點,則會給操作帶來很大困難。如何得到當前結點呢?我們定義了一個方法cursor(),返回值是指向當前結點的指標。類List還定義了一些方法來實現對連結串列的基本操作,透過運用這些基本操作我們可以對連結串列進行各種操作。例如reset()方法使第一個結點成為當前結點。insert(Object d)方法在當前結點前插入一個結點,並使其成為當前結點。remove()方法刪除當前結點同時返回其內容,並使其後繼結點成為當前結點,如果刪除的是最後一個結點,則第一個結點變為當前結點。
連結串列類List的原始碼如下:
public class List {private Node Head = null; private Node Tail = null; private Node Pointer = null; private int Length = 0;public void deleteAll() {Head = null; Tail = null; Pointer = null; Length = 0; }public void reset() {Pointer = null; }public boolean isEmpty() {return (Length == 0); }public boolean isEnd() {if (Length == 0)throw new java.lang.NullPointerException(); else if (Length == 1)return true; else return (cursor() == Tail); }public Object nextNode() {if (Length == 1)throw new java.util.NoSuchElementException(); else if (Length == 0)throw new java.lang.NullPointerException(); else {Node temp = cursor(); Pointer = temp; if (temp != Tail)return (temp.next.data); else throw new java.util.NoSuchElementException(); }}public Object currentNode() {Node temp = cursor(); return temp.data; }public void insert(Object d) {Node e = new Node(d); if (Length == 0) {Tail = e; Head = e; } else {Node temp = cursor(); e.next = temp; if (Pointer == null)Head = e; else Pointer.next = e; }Length++; }public int size() {return (Length); }public Object remove() {Object temp; if (Length == 0)throw new java.util.NoSuchElementException(); else if (Length == 1) {temp = Head.data; deleteAll(); } else {Node cur = cursor(); temp = cur.data; if (cur == Head)Head = cur.next; else if (cur == Tail) {Pointer.next = null; Tail = Pointer; reset(); } else Pointer.next = cur.next; Length--; }return temp; }private Node cursor() {if (Head == null)throw new java.lang.NullPointerException(); else if (Pointer == null)return Head; else return Pointer.next; }public static void main(String[] args) {List a = new List(); for (int i = 1; i <= 10; i++)a.insert(new Integer(i)); System.out.println(a.currentNode()); while (!a.isEnd())System.out.println(a.nextNode()); a.reset(); while (!a.isEnd()) {a.remove(); }a.remove(); a.reset(); if (a.isEmpty())System.out.println("There is no Node in List n"); System.out.println("You can press return to quitn"); try {System.in.read(); } catch (IOException e) {}}}class Node {Object data; Node next;Node(Object d) {data = d; next = null; }}
當然,雙向連結串列基本操作的實現略有不同。連結串列和雙向連結串列的實現方法,也可以用在堆疊和佇列的實現中
PYTHON眾所周知,C語言實現的連結串列是由一個一個的結點構成,每個結點分為資料域和指標域,指標域中儲存了其後繼結點的地址,透過地址來訪問下一個結點,然後一步一步的串聯起來形成了一個單鏈表。但是Python沒有指標啊,下面我們分析下它是怎麼實現的。
結點的實現
class Node(object):
"""節點"""
def __init__(self, elem):
self.elem = elem
self.next = None
畢竟Python中萬物皆物件嘛,一個結點用類的方式來實現也比較符合Python的語法結構,一如既往的那麼簡潔優雅,其實這個類的方法實現連結串列類似於C語言中的結構體實現連結串列不過其對於指向下一個結點的方式略有不同。
elem是資料域,next類似於C語言中的指標域,是下一個結點的標識。所以一個單一結點類物件的next指向None。
在這裡我要特別強調一下Python中的“=”的特殊含義以及作用。Python中的“=”是一種指向性的表示,self.next = None即節點的next指向為None,相當於我在None上貼了一個標籤,標籤的名字是next,這個next指向None。
單鏈表的操作
連結串列有以下操作方法,包括最基本的增刪改查和其他的一些方法。
is_empty() 連結串列是否為空
travel() 遍歷整個連結串列
length() 連結串列長度
add(item) 連結串列頭部新增元素
append(item) 連結串列尾部新增元素
insert(pos, item) 指定位置新增元素
search(item) 查詢節點是否存在
單鏈表操作的實現
一個單鏈表首先要有一個頭結點:
class SingleLinkList(object):
"""單鏈表"""
def __init__(self, node=None):
self.__head = node
is_empty()判斷連結串列是否為空
這個函式主要返回就是True和False,如果頭結點是空,那麼這個連結串列就是一個空連結串列.
def is_empty(self):
"""判斷連結串列是否為空"""
return self.__head == None
travel() 遍歷整個連結串列
cur是一個遊標,也就是一個標籤,開始指向頭結點,隨著遍歷迴圈的開始,只要這個結點沒有指向None,cur = cur.next遊標向下一個結點移動。
def travel(self):
"""遍歷整個連結串列"""
cur = self.__head
while cur != None:
print(cur.elem, end=" ")
cur = cur.next
print("")
length() 連結串列長度
連結串列長度的計算方式就是遍歷法,遍歷一個結點,只要這個結點不是None,那麼計數器+1。
def length(self):
"""連結串列長度"""
# cur遊標,用來移動遍歷節點
cur = self.__head
# count記錄數量
count = 0
while cur != None:
count += 1
cur = cur.next
return count
add(item) 連結串列頭部新增元素
向連結串列頭部新增元素很簡單,因為連結串列是一個活動性很大的資料結構,其中的每一個結點彼此只靠next相連線,其物理儲存之間沒啥太大的關係,在頭部新增元素我們只需要將這個結點放在原先的第一個結點之前,然後再讓頭結點指向這個新新增的結點就可以了。然後我們就要考慮一下連結串列是空的情況了,此時self.__head = None然後再將頭結點加上去,程式碼任然沒問題。
def add(self, item):
"""連結串列頭部新增元素,頭插法"""
node = Node(item)
node.next = self.__head
self.__head = node
append(item) 連結串列尾部新增元素
在連結串列的尾部新增元素也比較簡單首先假設連結串列非空,單鏈表只能由頭結點往後遍歷找到最後一個結點,其指向為None,也就是cur.next = None,然後將cur.next指向要新增的這個結點。如果是空連結串列,直接將頭結點指向要新增的這個節點就可以。
def append(self, item):
"""連結串列尾部新增元素, 尾插法"""
node = Node(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
insert(pos, item) 指定位置新增元素
pos是要新增的位置,即第幾個結點。先假設連結串列非空,並且是從中間插入結點。先建立一個指向頭結點的cur遊標,然後迴圈遍歷,並讓計數器count每遍歷一次次數加一,直到count=pos-1,這裡為什麼是pos-1而不是pos的原因是如果插入到3這個位置,其實cur遊標是停在2這裡,然後結點的next指向pos這個結點,cur的next則指向新增的這個結點,聽起來有點繞啊,大概過程就在下面這個圖裡,這個地方需要停下來好好想一想的。然後在考慮特殊情況,如果pos<=0得話,預設就是空節點,直接呼叫add函式即可,如果pos大於連結串列的長度,就是在尾部新增結點,呼叫apped函式。
def insert(self, pos, item):
"""指定位置新增元素
:param pos 從0開始
"""
if pos <= 0:
self.add(item)
elif pos > (self.length()-1):
self.append(item)
else:
cur = self.__head
count = 0
while count < (pos-1):
count += 1
cur = cur.next
# 當迴圈退出後,pre指向pos-1位置
node = Node(item)
node.next =cur.next
cur.next = node
search(item) 查詢節點是否存在
遍歷查詢即可。
def search(self, item):
"""查詢節點是否存在"""
cur = self.__head
while cur != None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
先假設刪除的結點是中間的部分,前後均有結點與其相連。為了便於理解,這裡我們整了兩個遊標pre和cur,其中pre指向None,cur指向頭結點,然後執行pre = cur,cur = cur.next就能夠一步步的將兩個遊標向前移動,並且始終各自指向相連的兩個結點。當cur指向要刪除的結點時,執行pre.next = cur.next,這裡是什麼意思呢?cur的next是要刪除節點的下一個結點,怎樣才算刪除呢?當我們這個結點與真個連結串列沒有關係的時候就算是刪除了,pre的next如果跳過我們刪除的結點,直接指向其下一個結點,不就是把這個結點拋棄了,使其與連結串列沒有關係,然後刪除了嘛!如果這個結點是第一個頭結點,刪除他的話就比較簡單了,直接把頭結點__head指向要刪除節點的下一個結點不就OK了嘛。
def remove(self, item):
cur = self.__head
pre = None
while cur != None:
if cur.elem == item:
# 先判斷此結點是否是頭節點
# 頭節點
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
測試程式碼
if __name__ == "__main__":
ll = SingleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.add(8)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
ll.insert(-1, 9)
ll.travel()
ll.insert(3, 100)
ll.travel()
ll.insert(10, 200)
ll.travel()
ll.remove(100)
ll.travel()
ll.remove(9)
ll.travel()
ll.remove(200)
ll.travel()
回覆列表
其實python和java底層還是c和c++的。只不過作為高階語言,把指標封裝起來,不向使用者暴露,另外語言自己提供垃圾回收機制。
這就好比自動擋汽車和手動擋汽車。雖然都需要換擋,但是自動擋汽車把換擋邏輯用變速器自己實現了,使用者只需要一直掛著D擋,加速減速就可以。但是底層還是要不斷換擋的。