データ構造注1:リスト
知っているメインリストの必要性は、最初のリストのテール補間補間を作成します。削除操作を挿入します。トラバースがあり、
非常にシンプル。
最も重要なポイントは、良いヘッドノードの適用を検討しています。
Node.h
#ifndef _NODE_H_
#define _NODE_H_
//结构体一般定义在.h文件中
#define ElemType int
typedef struct LNode //单链表节点
{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
/*与上面等价
struct LNode{
ElemType data;
struct LNode *next;
};
typedef struct LNode LNode; //LNode就是 struct LNode的一个别名
typedef struct LNode* LinkList; //LinkList是struct LNode*的一个别名
LNode a;//声明了一个struct LNode型变量a,与写 struct LNode a; 等价;
LinkList p;//声明了一个struct LNode *型指针变量p,与写 struct LNode *p; 等价。
*/
typedef struct DNode //双链表节点
{
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinklist;
#endif
LinkList.h
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#include"Node.h"
using namespace std;
//单链表
LinkList LinkList_HeadInsert_Init(LinkList &L);
LinkList LinkList_TailInsert_Init(LinkList &L);
void LinkList_All_Out(LinkList L);
LinkList LinkList_Search(LinkList L,int i);
void LinkList_Insert(LinkList L,int i);
void LinkList_Delete(LinkList L,int i);
int LinkList_GetLenth(LinkList L);
//双链表
DLinklist DLinklist_Init(DLinklist &D);
void DLinklist_All_Out(DLinklist D);
DLinklist DLinklist_Search(DLinklist D,int i);
void DLinklist_Insert(DLinklist D,int i,bool j);
//这次写的函数都是传入的头结点,但是事实上很多情况下需要传入的
//是中间某个节点返回的p指针,比如先用search函数得到对应的指针。
#endif
LinkList.cpp
#include <iostream>
#include "LinkList.h"
#include "Node.h"
using namespace std;
/****************************单链表*********************************/
//最核心的思想,头结点的处理如何进行
//头插法,但是得到的元素和输入的元素是倒序的
LinkList LinkList_HeadInsert_Init(LinkList &L)
//引用,改变函数内L的值函数返回值也发生改变,同时引用定义了L,无需重新定义可直接调用
//需要对传入的参数进行改变时就要用引用
{
LinkList p;
int x;
L = new(LNode); //头结点,不存放数据 ,
//分配的内存是用来存放节点的,然后让一个指针指向这段内存。
L->next = NULL;
cin >> x;
while(x != 9999)
{
p = new(LNode); //创建新节点
p->data = x;
p->next = L->next; //插在头结点和现在第一个节点之间变成第一个节点
L->next = p;
cin >> x;
}
return L ;
}
//尾插法
LinkList LinkList_TailInsert_Init(LinkList &L)
{
int x;
LinkList p,r; //尾插法需要多定义一个指针作为中间变量
L = new(LNode);//要先分配空间,再把L赋给r
r = L;
cin >> x;
while(x != 9999)
{
p = new(LNode); //创建新节点
p->data = x;
r->next = p;
r = p;
cin >> x;
}
r->next = NULL;//尾插法让最后一个节点指向空即可
return L;
}
//遍历所有元素并全部输出
void LinkList_All_Out(LinkList L)
{
LinkList p = L->next;
while(p != NULL)
{
cout<< p->data <<endl;
p = p->next;
}
}
//访问特定元素并输出
LinkList LinkList_Search(LinkList L,int i)
{
LinkList p = L->next;
int j = 1;
if(i == 0) return L;
if(i<1) return NULL;
while(p->next) //判断条件(p->next)为真等价于判断(p->next != NULL)为真
{
if(j == i) break; //遍历+下标
j++;
p = p->next;
}
return p;
}
void LinkList_Insert(LinkList L,int i) //插入节点
{
LinkList p,q;
int x;
p = LinkList_Search(L,i);
q = new(LNode);
cin >> x ;
q->data = x;
q->next = p->next;
p->next = q;
}
void LinkList_Delete(LinkList L,int i)
{
int j = 1;
LinkList p,q;
p = L;
while(p && j < i)
{
j++;
p = p->next;
}
q = p->next; //这里为什么要把p->next保存给q呢
//因为我们最后要把删去的节点对应的内存给释放,注意释放的是指针指向的内存而并非指针本身。
//而执行了 p->next = p->next->next 之后,我们就无法找到该节点这段内存了,用q保存p->next,
//本质上保存的是这段内存,因此最后直接释放q,就相当于释放掉这段内存了。初始化q时也无需给q分配内存。
p->next = p->next->next;
delete q; //delete()和free()的操作对象是指针,清除的是指针指向的内存。
}
int LinkList_GetLenth(LinkList L)
{
LinkList p = L->next;
int n = 0;
while(p)
{
n++;
p = p->next;
}
return n;
}
/**************************************************************/
/**************************双链表*******************************/
DLinklist DLinklist_Init(DLinklist &D)
{
DLinklist p,q;
int x;
D = new(DNode);//建空双向循环链表
D->next = D;
D->prior = D;
q = D;
cin>>x;
while(x != 9999)
{
p = new(DNode);
p->prior = q;
q->next = p;
p->next = D;
D->prior = p;
p->data = x;
q = p;
cin>>x;
}
return D;
}
void DLinklist_All_Out(DLinklist D)
{
DLinklist p = D->next;
while(p != D)
{
cout<< p->data <<endl;
p = p->next;
}
}
DLinklist DLinklist_Search(DLinklist D,int i)
{
DLinklist p = D->next;
int j = 1;
if(i == 0) return D;
if(i<1) return NULL;
while(p->next) //判断条件(p->next)为真等价于判断(p->next != NULL)为真
{
if(j == i) break; //遍历+下标
j++;
p = p->next;
}
return p;
}
void DLinklist_Insert(DLinklist D,int i,bool j) //插入节点,bool为0,
//插入在i节点之后,为1插在之前
{
DLinklist p,q;
int x;
p = DLinklist_Search(D,i);
q = new(DNode);
cin >> x ;
q->data = x;
if(j == 0) //后插
{
p->next->prior = q;//先链接新增节点和未被移动指针p指向的节点
q->next = p->next; //1和2指令可对调,3和4指令可对调,
//但12要在34之前执行
p->next = q;
q->prior = p;
}
else //前插
{
p->prior->next = q;
q->prior = p->prior;
p->prior = q;
q->next = p;
}
}
void DLinklist_Delete(DLinklist D,int i) //删除访问到的那个节点
{
int j = 1;
DLinklist p,q;
p = D;
while(p && j < i)
{
j++;
p = p->next;
}
q = p->next;
p->next = p->next->next;
delete q;
}
main.cppに
#include <iostream>
#include "LinkList.h"
using namespace std;
// int main()
// {
// LinkList L;
// int num;
// //L = LinkList_HeadInsert_Init(L); //引用时在定义里用到了&L,因此在调用函数时直接将L传参进去即可
// L = LinkList_TailInsert_Init(L);
// //LinkList_All_Out(L);
// //p = LinkList_Search(L,5);
// //cout<<p->data<<endl;
// LinkList_Insert(L,5);
// LinkList_All_Out(L);
// LinkList_Delete(L,4);
// LinkList_All_Out(L);
// num = LinkList_GetLenth(L);
// cout << num << endl;
// //cout << L->next->next->data<<endl;
// return 0;
// }
#define Next 0
#define Prior 1
int main()
{
DLinklist D;
D = DLinklist_Init(D);
// cout << D->next->data <<endl;
// cout << D->prior->data <<endl;
DLinklist_Insert(D,5,Prior);
DLinklist_All_Out(D);
return 0;
}
エラーが発生しやすい仕上げ:
合計の最初のリスト要素ノードの違い、最初のノード、先頭ポインタ
3の基本的な考え方:
図1に示すように、第一の要素ノード:ノードは、第1のデータ要素A1に記憶されたリストを指します。
図2に示すように、最初のノード:前のメタデータの最初のセットにノードであるノード、最初のノードへのメタデータポインタフィールドポイント。ヘッド
ノードデータフィールドは、情報を保存しないことがあり、付加情報データ要素の他のタイプの格納されてもよいです。
図3に示すように、ヘッド・ポインタ:これはリンクされたリスト内の最初のノードへのポインタです。リストは最初のノードを備えている場合は、最初に
リニアテーブルのヘッドノードへのノードのポインタ、第一のノードは、リンクされたリストを持っていない場合、ノードラインの先頭ポインタ
ノードテーブルの最初の要素。
図4に示すように、テール・ポインタ:ターミナルノードのリンクリストへのポインタ。
リストのヘッドノードの役割を増やし、次のとおりです。
図1に示すように、ヘッドノード(「前駆体」ノードと呼ばれている)ヘッダ・ノードに格納された第1ノード元アドレスを増加させます
レイドなしのポインタ・フィールド、リストの最初のデータ要素の動作の他のデータ要素と同様、
特別な治療
図2に示すように、テーブルは簡単空に非空のテーブル一体ではない、最初のノードがリストLの単一ヘッドと仮定するないリスト
空リスト0の単一鎖長であるべきであり、場合n個の第1要素ノードを指すポインタが、Lポインタがヌルである(決定
条件は空のテーブルと呼ぶことができる:L == NULL)
3にかかわらず、リストが空であるかどうかを鎖場合、ヘッドポインタは、非ヌル・ポインタの最初のノードを指しているの最初のノードの増加、
テーブルは、次に、ヘッドノードポインタフィールドが空である、空です。
第二に、ラウンドロビンリストのテールポインタ
単一サイクルでリストが、テールポインタは、一般的にヘッド・ポインタの代わりに使用されています。
テール・ポインタは、チェーンがリストの先頭を見つけることができるように、単一サイクルで表される終端ノードを指すポインタであります
簡単にノードおよび終端ノード。
夫々リアテールポインタ、開始ノードと終了ノード位置であり、リード線セットノードリスト単一サイクル、
リア>ネクスト>隣にあり、リア、検索時間はO(1)です。ヘッドポインタは、リストを示すためにした場合、検索されます
端末ノード時間はO(N)です。
第三には、リスト上の最後の要素を削除します
前駆体ノード終端ノードにリストニーズへのアクセスの最後の要素を削除するので、ノードポイントを作ります
NULL、端末ノード自由落下は、その二重リンクリストの先行ノードへのアクセスを容易にするために使用する必要があります。
実際には、最初の要素は同一の理由は柔軟分析であるべきであり、削除キーは、削除または挿入操作が必要とされます
これは最初のアクセス・ノード、何でリストへの最も簡単なアクセス。
第四に、基準質量参加及び質量参加ポインタに
挿入ノードの動作を見てください:
void LinkList_Insert(LinkList L,int i) //传入指针
或者
void LinkList_Insert(LinkList &L,int i) //引用操作
{
LinkList p,q;
int x;
p = LinkList_Search(L,i);
q = new(LNode); //给形参分配内存
cin >> x ;
q->data = x;
q->next = p->next;
p->next = q;
}
この場合、これら2つの操作が、実際には本質的な違いはありません。しかし、時には、ポインタパラメータ渡しと参照
または異なる、差別以下の何かをします。
図1は、関数へのポインタを渡すの効果への参照を渡すことと同じです。この場合、呼び出される関数のパラメータはなり
使用された元の呼び出し元の関数の引数変数やオブジェクト内の別名、いわゆる関数内のパラメータ
操作変数(関数呼び出しに)その操作に対応する目標オブジェクトです。
図2は、パラメータの基準伝達関数を用いて、直接メモリに引数のコピーを生成しませんでした
引数の操作、一般的な変数関数を使用して、パラメータ転送、関数呼び出しが発生した場合、パラメータに必要です
ストレージユニットに割り当てる、変数パラメータは、引数変数のコピーがあり、オブジェクトが渡された場合、コピーを呼び出します
コンストラクタ。このように、大きなデータ伝送パラメータ、一般的な可変比率伝送パラメータの影響を参照して
速度とフットプリントが良いです。
3、効果があること、しかし、参照を使用しても達成することができるが、パラメータの関数としてポインタを使用して
同じ調整パラメータの割り当て記憶部を与える機能、および再利用フォームに必要「*ポインタ変数名」されます
操作、貧しい人々を読み、エラー手順は非常に簡単である。一方で、呼び出す関数呼び出しのポイント
、変数のアドレスを引数として使用されます。参照が容易に、より明確に、使用します。
図4は、それが参照するプログラムの利用効率を向上させるために、だけでなく、関数に渡されたデータを保護する必要がある場合、関数ではありません
変更された、あなたは一定の参照を使用する必要があります。
const int &d = c;
//常引用 是让 变量引用 具有只读属性 不能通过d 去修改c,但改变c会让d的值变