1. 程式人生 > >連結串列,順序表的基本操作及其實現的棧與佇列

連結串列,順序表的基本操作及其實現的棧與佇列

1,順序表

在c裡面,這一部分也就是靜態表和動態表的實現,插入,刪除有丁點意思。

靜態表靠陣列和長度實現(注意長度這一點,因為它反映了當前表內容長度,十分重要)

動態表依靠申請連續空間實現(結構體內部包括首地址,當前規模以及長度)

插入:

	int *p;
	p=&(w.a[k-1]);
	int *q;
	for(q=&(w.a[w.length-1]);q>=p;--q)
	{
		*(q+1)=*q;
	}
	*q=m;
	++w.length;
刪除同理

2,連結串列:

連結串列結構體很簡單:

typedef struct Node{
	int data;
	struct Node *next;
}; 
typedef Node* Linkedlist;

首先是首插法和尾插法:

int Touinsert(Linkedlist &l)
{
	Lnode* r,s,m;
	l=r;
	for(int i=0;i<=length;i++)
	{
		s=(Lnode *)malloc(sizeof(Lnode));
		s->data=i;
		s->next=L;
		r=L;        //!
	}
	for(int i=0;i<=length;i++)
	{
		s=(Lnode *)malloc(sizeof(Lnode));
		s->data=i;
		L->next=s;
		s->next=m;
		m=s;
	} 
}//頭插法對於存不存在頭節點沒有特殊需求,反而頭結點會導致頭插法不那麼方便
/*
int Weiinsert(LInkedlist &l)
{
	Lnode *r,m;
	r=L;
		for(int i=0;i<=length;i++)
	{
		s=(Lnode *)malloc(sizeof(Lnode));
	    r->next=s;
	    r=s;
	}
}//尾插法在此處就有問題——我們不可能r=L後(此時L為NULL)通過對r的操作改變L,首結點的本質是使得初定義的指標可以和L共同指向一個有意義的節點,通過初定義的指標直接影響L
*/ 
int Weiinsert(LInkedlist *l)
{
	L=(Lnode*)malloc(sizeof(Lnode));//注意定義首節點的方法 
	Lnode *r,*s;
	r=L;
	for(int i=0;i<=length;i++)
	{
		s=(Lnode *)malloc(sizeof(Lnode));
		s->data=i;
		r->next=s;
		r=s;
	}
}
如上所述,尾插法記得加首節點,會令操作簡化一些,並且邏輯不會衝突。

接下來,是迴圈連結串列,雙鏈表,迴圈雙鏈表的基本操作,並不複雜,也就是在基本的尾插法上加一些操作即可實現。

void init_CircleLinkedList(Linkedlist &l1,int t)
{
	
	Linkedlist m,a;
	m=(Linkedlist)malloc(sizeof(Node));
	m->data=t;
	l1=m;               //!!!
	m->next=NULL;
	
	for(int i=0;i<t;i++)
	{
		a=(Linkedlist)malloc(sizeof(Node));
		a->data=i;
		m->next=a;
		m=m->next;
	
		if(i==t-1)
		{
			m->next=l1->next;
			continue;
		}
	}
}
void init_DoubleLInkedlist(Linkedlist1 &l2,int t)
{
	Linkedlist1 m,a;
	m=(Linkedlist1)malloc(sizeof(Node1));
	m->data=t;
	l2=m;
	m->next1=NULL;
	m->prior=NULL;
	for(int i=0;i<t;i++)
	{
		a=(Linkedlist1)malloc(sizeof(Node1));
		a->data=i;
		m->next1=a;
		a->prior=m;
        m=a;
        if(i==t-1)
        {
        	m->next1=NULL;
        }
	}
}
void init_DoubleLCircleInkedlist(Linkedlist1 &l2,int t)
{
	Linkedlist1 m,a;
	m=(Linkedlist1)malloc(sizeof(Node1));
	m->data=t;
	l2=m;
	m->next1=NULL;
	m->prior=NULL;
	for(int i=0;i<t;i++)
	{
		a=(Linkedlist1)malloc(sizeof(Node1));
		a->data=i;
		m->next1=a;
		a->prior=m;
        m=a;
        if(i==t-1)
        {
        	m->next1=l2;
        	l2->next->prior=m;
        }
	}
}
最後,我特別貼上一張雙鏈表的插入操作的圖:

注意:在雙向連結串列中,得到節點只能通過priori或是next得到先序節點或是後繼節點,所以我們在連好後/先節點與插入節點之前不可斷鏈。

概括而言:順序是s的後繼連向p的後繼,p的後繼的前驅連上s(先把p的後繼處理),s的前驅指向p,p的後繼指向s。(之後把p連向s);

3,棧與佇列

棧和佇列基於順序表抑或是連結串列實現的,他們不過是在後者的操作的基礎上加上特定的操作罷了。

typedef struct{
	char a[MAXSIZE];
	int top;
}SqStack;
typedef struct{
	int *base;
	int *top;
	int stacksize;
}SqStack1;

以上是順序棧的結構體,靜態的top初始化為-1,動態的top=base指向申請空間首位置。

注意:入棧是先升指標再入棧。(*(++S.top)=e)

           出棧是先賦值給引數再降指標(*(S.top--)=e)(反了,懶得改能看懂就ok)

鏈棧不很常見,頭節點+首插法就ok,存取物件都是頭節點的next即可。

佇列:

以普通靜態佇列為例:

typedef struct{
	int elem[100];
	int rear,front;
	int SqSize;
}SqQueue;
以上結構體;

入隊出隊:

void Basic_Operate(SqQueue &S,int a,int m)
{
	if(m==1)
	{
		S.elem[S.top++]=a;
	}
	if(m==0)
	{
		a=S.elem[S.rear++];
		printf("%d",a);
	}
}
迴圈動態佇列:
typedef struct{
	int *base;
	int top;
	int rear;
}CircleQueue;
以上結構體;
int init_CQ(CircleQueue &CQ)
{
	CQ.base=(int *)malloc(sizeof(100));
	CQ.top=0;
	CQ.rear=0;
}
int EnQueue(Circle &CQ,int a)
{
	if((CQ.rear+1+100)%100==CQ.top){
		return ERROR;
	}
	else
	{
		CQ.base[CQ.rear]=a;
		CQ.rear=(CQ.rear+1+100)%100;
		return 1;
	}
}
int DeQueue(CirCleQueue &CQ,int a)
{
	if(CQ.top==CQ.rear)
	{
		return ERROR;
	}
	else
	{
		a=CQ.base[top];
		CQ.top=(CQ.top+1+100)%100;
		return a;
	}
}

以上初始化/插入/刪除操作;

注意:1,判滿條件:(CQ.rear+1)%MAXSIZE==CQ.top;

           2,判空條件:CQ.rear==CQ.top;

           3,CQ.top=(top+1)%MAXSIZE;

最後,在腦海裡要形成棧和佇列各自指標的位置,棧只有一個頭指標top,永遠指向存在值的最上面的地址,佇列有兩個指標,首指標和尾指標,尾進頭出,尾指標永遠指向最後一個入隊元素下一個的空位置,頭指標永遠指向即將出隊的元素,除非隊空,一定有值。