一、多項式的表示
1.1 一元多項式及其運算
一元多項式:\(f(x)=a_0+a_1x+\cdots+a_{n-1}x^{n-1}+a_nx^n\)
主要運算:多項式相加、相減、相乘等
如何用程序設計語言表示多項式,並且實現對多項式的操作?
1.2 如何表示多項式
- 多項式的關鍵數組
- 多項式項數\(n\)
- 各項系數\(a_i\) 及指數 \(i\)
1.3 方法1:順序存儲結構直接表示
數組各分量對應多項式各項:a[i]
表示項\(x^i\)的系數\(a_i\)
例如:\(f(x)=4x^5-3x^2+1\)
表示如下圖所示:
兩個多現實相加:兩個數組對應分量相加
問題:如何表示多項式\(x+3x^{2000}\),至少要有2001個分量表示,並且20001個分量中只有兩項是非零的,這樣的表示方法是有很大問題的
1.4 方法2:順序存儲結構表示非零項
每個非零項\(x_ix^i\)涉及兩個信息:系數\(a_i\)和指數\(i\)
可以將一個多項式看成是一個\((a_i,i)\)二元組的集合。
用結構數組表示:數組分量是由系數\(a_i\)、指數\(i\)組成的結構,對應一個非零項
例如:\(P_1(x)=9x^{12}+15x^8+3x^2\)和\(P_2(x)=26x^{19}-4x^8-13x^6+82\)
按指數大小有序存儲!
相加過程:從頭開始,比較兩個多項式當前對應項的指數
$ P1: (9,12), (15,8), (3,2) $
$ P2: (26,19), (-4,8), (-13,6), (82,0) $
$P3: (26,19) (9,12) (11,8) (-13,6) (3,2) (82,0) $
\(P_3(x)=26x^{19}+9x^{12}+11x^8-13x^6+3x^2+82\)
1.5 方法3:鏈表結構存儲非零項
鏈表中每個結點存儲多項式中的一個非零項,包括系數和指數兩個數據域寄一個指針域
/* c語言實現 */
typedef struct PolyNode *Polynomial;
struct PolyNode{
int coef;
int expon;
Polynomial link;
}
# python語言實現
class PolyNode():
def __init__(coef, expon):
self.coef = coef
self.expon = expon
self.next = None
例如:
鏈表存儲形式為:
鏈表形式表現的多項式加法過程類似於前兩種方法。
二、什么是線性表
多項式表示問題的啟示:
- 同一個問題可以有不同的表示(存儲)方法
- 有一類共性問題:有序線性序列的組織和管理
“線性表(Linear List)”:由同類型數據元素構成有序序列的線性結構
- 表中元素個數稱為線性表的長度
- 線性表沒有元素時,稱為空表
- 表起始位置稱表頭,表結束位置稱表尾
三、線性表的抽象數據類型描述
類型名稱:線性表(List)
數據對象集:線性表是\(n(\geq{0})\)個元素構成的有序序列\((a_1,a_2,\dots,a_n)\)
操作集:線性表\(L\in{List}\),整數\(i\)表示位置,元素\(X\in{ElementType\),線性表基本操作主要有:
List MakeEmpty():
初始化一個空線性表\(L\);ElementType FindKth( int K, List L ):
根據位序\(K\),返回相應元素 ;int Find( ElementType X, List L ):
在線性表\(L\)中查找\(X\)的第一次出現位置;void Insert( ElementType X, int i, List L):
在位序\(i\)前插入一個新元素\(X\);void Delete( int i, List L ):
刪除指定位序\(i\)的元素;int Length( List L ):
返回線性表\(L\)的長度\(n\)。
四、線性表的順序存儲實現
利用數組的連續存儲空間順序存放線性表的各元素
/* c語言實現 */
typedef struct LNode *List; /* 定義結構體指針 */
struct LNode{
ElementType Data[MAXSIZE]; /* 數組類型的Data,數組最大長度為MAXSIZE */
int Last;
}; /* 定義結構體 */
struct LNode L; /* 聲明變量L */
List PtrL; /* 聲明結構體PtrL */
訪問下標為\(i\)的元素:L.Data[i]
或PtrL->Data[i](取出PtrL所指向的結構體中包含的數據項Data[i])
線性表的長度:L.Last+1
或PtrL->Last+1(取出PtrL所指向的結構體中包含的數據項Last並加1)
主要操作的實現
4.1 初始化(建立空的順序表)
/* c語言實現 */
List MakeEmpty()
{
List PtrL;
PtrL = (List)malloc(sizeof(struct LNode)); /* 申請一個結構體 */
PtrL->Last = -1;
return PtrL;
}
4.2 查找
查找成功的平均比較次數為\((n+1)/2\),平均時間性能為\(O(n)\)
/* c語言實現 */
int Find(ElementType X, List Ptrl)
{
int i = 0;
while (i <= Ptrl->Last && Ptrl->Data[i] != X)
i++;
if (i > Ptrl->Last) return -1; /* 如果沒找到,返回-1 */
else return i; /* 找到后返回的事存儲位置 */
4.3 插入(第\(i(I\leq{I}\leq{n+1}\))個位置上插入一個值為\(X\)的新元素)
平均移動次數為\(n/2\),平均時間性能為\(O(n)\)
/* c語言實現 */
void Insert(ElementType X, int i, List PtrL)
{
int j;
if (Ptrl->Last == MAXSIZE - 1){ /* 表空間已滿,不能插入 */
printf("表滿");
return ;
}
if (i<1 || PtrL->Last+2){
printf("位置不合法");
return ;
}
for (j=PtrL->Last; j>=i-1; j--)
PtrL->Data[j+1] = Ptrl->Data[j]; /*將a_i~a_n倒序向后移動*/
PtrL->Data[i-1] = X; /* 新元素插入 */
PtrL->Last++; /* Last仍指向最后元素 */
return;
}
4.4 刪除(刪除表的第\(i(1\leq{i}\leq{n})\)個位置上的元素)
平均移動次數為\((n-1)/2\),平均時間性能為\(O(n)\)
/* c語言實現 */
void Delete(int i, List Ptrl)
{
int j;
if(i<1 || i>PtrL->Last+1){ /* 檢查空表及刪除位置的合法性 */
printf("不存在第%d個元素", i);
return ;
}
for (j=i, j<=Ptrl->Last; j++)
PtrL->Data[j-1] = Ptrl->Data[j]; /* 將a_{i+1}~a_n順序向前移動*/
Ptrl->Last--; /* Last仍指向最后元素 */
return;
}
五、線性表的鏈式存儲實現
不要求邏輯上相鄰的兩個元素物理上也相鄰;通過“鏈”建立起數據元素之間的邏輯關系。即插入、刪除不需要移動數據元素,只需要修改“鏈”。
/* c語言實現 */
typedef struct LNode *List;
struct LNode{
ElementType Data;
List Next;
};
struct Londe L;
List PtrL;
5.1 求表長
時間性能為\(O(n)\)
/* c語言實現 */
int Length(List PtrL)
{
List p = PtrL; /* p指向表的第一個結點 */
int j = 0;
while (p) {
p = p->Next;
j++; /* 當前p指向的是第j個結點 */
}
return j;
}
5.2 查找:
平均時間性能為\(O(n)\)
5.2.1 按序號查找:FindKth;
/* c語言實現 */
List FindKth(int K, List PtrL)
{
List p = Ptrl;
int i = 1;
while (p != NULL && i < K){
p = p->Next;
i++;
}
if (i==K) return P; /* 找到第K個,返回指針 */
else return NULL; /* 否則返回空 */
5.2.2 按值查找:Find
/* c語言實現 */
List Find(ElementType X, List PtrL)
{
List p = PtrL;
while (p != NULL && p->Data != X)
p = p->Next;
return p;
}
-
插入(在第\(i-1(1\leq{i}\leq{n+1})\)個結點后插入一個值為\(X\)的新結點)
-
先構造一個新結點,用s指向;
-
再找到鏈表的第\(i-1\)個j結點,用\(p\)指向;
-
然后修改指針,插入結點(\(p\)之后插入新結點是\(s\))
-
/* c語言實現 */
List Insert(ElementType X, int i, List PtrL)
{
List p, s;
if (i == 1){ /* 新結點插入在表頭 */
s = (List)malloc(sizeof(struct LNode)); /* 申請、填裝結點 */
s->Data = X;
s->Next = Ptrl;
return s; /* 返回新表頭指針 */
}
p = FindKth(i-1, Ptrl); /* 查找第i-1個結點 */
if (p == NULL){ /* 第i-1個不存在,不能插入 */
printf("參數i錯");
return NULL;
}else{
s = (List)malloc(sizeof(struct LNode)); /* 申請、填裝結點 */
s->Data = X;
s->Next = p->Next; /* 新結點插入在第i-1個結點的后面*/
p->Next = s;
return PtrL;
}
5.3 刪除(刪除鏈表的第\(i(1\leq{i}\leq{n})\)個位置上的結點)
平均查找次數為\(n/2\),平均時間性能為\(O(n)\)
- 先找到鏈表的第\(i-1\)個結點,用\(p\)指向
- 再用指針\(s\)指向要被刪除的結點(\(p\)的下一個結點);
- 然后修改指針,刪除\(s\)所指結點;
- 最后釋放\(s\)所指結點的空間。
/* c語言實現 */
List Delete(int i, List PtrL)
{
List p, s; /* 若要刪除的事表的第一個結點 */
if (i == 1){
s = PtrL; /* s指向第1個結點 */
if (PtrL != NULL) PtrL = PtrL->Next; /* 從鏈表中刪除 */
else return NULL;
free(s); /* 釋放被刪除結點 */
return PtrL;
}
p = FindKth(i-1, PtrL); /* 查找第i-1個結點 */
if (p == NULL){
printf("第%d個結點不存在", i-1); return NULL;
} else if (i->Next == NUll){
printf("第%d個結點不存在", i); return NULL;
} else {
s = p->Next; /* s指向第i個結點 */
p->Next = s->Next; /* 從鏈表中刪除*/
free(s); /* 釋放被刪除結點*/
return PtrL;
}
六、二元多項式的表示
我們知道了一元多項式的表示,那么二元多項式又該如何表示?比如,給定二元多項式:\(P(x,y)=9x^{12}y^2+4x^{12}+15x^8y^3-x^8y+3x^2\)
可以將上述二元多項式看成關於\(x\)的一元多項式:\(P(x,y)=(9y^2+4)x^{12}+(15y^3-y)x^8+3x^2\quad(ax^{12}+bx^8+cx^2)\)
因此,上述二元多項式可以用“復雜”鏈表表示為下圖所示:
七、廣義表
- 廣義表是線性表的推廣
- 對於線性表而言,\(n\)個元素都是基本的單元素;
- 廣義表中,這些元素不僅可以是單元素也可以是另一個廣義表。
/* c語言實現 */
typedef struct GNode *GList;
struct GNode{
int Tag; /* 標志域:0表示結點是單元素,1表示結點是廣義表 */
union{ /* 字表指針域Sublist與單元素數據域Data復用,即公用存儲空間 */
ElementType Data;
Glist SubList;
}URegion;
Glist Next; /* 指向后繼結點 */
}
八、多重鏈表
多重鏈表:鏈表中的結點可能同時隸屬於多個鏈
- 多重鏈表中結點的指針域會有多個,如前面例子包含了\(Next\)和\(SubList\)兩個指針域;
- 但包含兩個指針域的鏈表並不一定是多重鏈表,比如在雙線鏈表不是多重鏈表。
多重鏈表有廣泛的用途:基本上如樹、圖這樣相對復雜的數據結構都可以采用多重鏈表方式實現存儲。
8.1 例1:多重鏈表表示矩陣
矩陣可以用二維數組表示,但二維數組表示有兩個缺陷:
- 一是數組的大小需要事先確定,
- 對於“稀疏矩陣”,將造成大量的存儲空間浪費。
分析:采用一種典型的多重鏈表——十字鏈表來存儲稀疏矩陣
-
只存儲矩陣非0元素相:結點的數據域:行坐標\(Row\)、列坐標\(Col\)、數值\(Value\)
-
每個結點通過兩個指針域,把同行、同列串起來;
-
行指針(或稱為向右指針)Right
-
列指針(或稱為向下指針)Down
-
下圖為矩陣A的多重鏈表圖:
用一個標識域\(Tag\)來區分頭結點和非0元素結點;
頭結點的標識值為“Head”,矩陣非0元素結點的標識值為“Term”。
九、Python實現-單鏈表
class Node(object):
def __init__(self, val, p=0):
self.data = val
self.next = p
class LinkList(object):
def __init__(self):
self.head = 0
def __getitem__(self, key):
if self.is_empty():
print('linklist is empty.')
return
elif key < 0 or key > self.getlength():
print('the given key is error')
return
else:
return self.getitem(key)
def __setitem__(self, key, value):
if self.is_empty():
print('linklist is empty.')
return
elif key < 0 or key > self.getlength():
print('the given key is error')
return
else:
self.delete(key)
return self.insert(key)
def initlist(self, data):
self.head = Node(data[0])
p = self.head
for i in data[1:]:
node = Node(i)
p.next = node
p = p.next
def getlength(self):
p = self.head
length = 0
while p != 0:
length += 1
p = p.next
return length
def is_empty(self):
if self.getlength() == 0:
return True
else:
return False
def clear(self):
self.head = 0
def append(self, item):
q = Node(item)
if self.head == 0:
self.head = q
else:
p = self.head
while p.next != 0:
p = p.next
p.next = q
def getitem(self, index):
if self.is_empty():
print('Linklist is empty.')
return
j = 0
p = self.head
while p.next != 0 and j < index:
p = p.next
j += 1
if j == index:
return p.data
else:
print('target is not exist!')
def insert(self, index, item):
if self.is_empty() or index < 0 or index > self.getlength():
print('Linklist is empty.')
return
if index == 0:
q = Node(item, self.head)
self.head = q
p = self.head
post = self.head
j = 0
while p.next != 0 and j < index:
post = p
p = p.next
j += 1
if index == j:
q = Node(item, p)
post.next = q
q.next = p
def delete(self, index):
if self.is_empty() or index < 0 or index > self.getlength():
print('Linklist is empty.')
return
if index == 0:
q = Node('', self.head)
self.head = q
p = self.head
post = self.head
j = 0
while p.next != 0 and j < index:
post = p
p = p.next
j += 1
if index == j:
post.next = p.next
def index(self, value):
if self.is_empty():
print('Linklist is empty.')
return
p = self.head
i = 0
while p.next != 0 and not p.data == value:
p = p.next
i += 1
if p.data == value:
return i
else:
return -1
l = LinkList()
l.initlist([1, 2, 3, 4, 5])
print(l.getitem(4)) # 5
l.append(6)
print(l.getitem(5)) # 6
l.insert(4, 40)
print(l.getitem(3)) # 4
print(l.getitem(4)) # 40
print(l.getitem(5)) # 5
l.delete(5)
print(l.getitem(5)) # 6
l.index(5)