引言:
栈的另外一种存储方式是链式存储,即所谓的链栈(Linked Stack)。链栈通常用单链表来表示,其实可以看作单链表的简化。所以,链栈结点的结构与单链表结点的结构一样,而由于链栈的操作只是在一端进行,为了操作方便,把初始化时栈顶设在链表的头部,并且不需要头结点。另外为了快速计算出链栈的长度,特意增加了一个成员变量用于保存栈的长度(由于栈只能访问栈顶的数据元素,而链栈的栈顶指示器又不能指示栈的数据元素的个数。所以,求链栈的长度时,必须把栈中的数据元素一个个出栈,每出栈一个数据元素,计数器就增加1,但这样会破坏栈的结构。为保留栈中的数据元素,需把出栈的数据元素先压入另外一个栈,计算完长度后,再把数据元素压入原来的栈。但这种算法的空间复杂度和时间复杂度都很高,所以,以上两种算法都不是理想的解决方法。理想的解决方法是LinkSeqStack类增设一个字段nodeNum表示链栈中结点的个数)。
链栈节点类定义
namespace DataStructureLearning.ImplementClass
{
//链表节点类
public class LinkNode<T>
{
#region ----成员变量-----
private T data;//数据域,当前结点的值
private LinkNode<T> next;//指针域,指向下一个结点
#endregion
#region ----构造方法-----
//构造方法
public LinkNode(T val, LinkNode<T> p)
{
data = val;
next = p;
}
//构造方法只包含引用域的结点
public LinkNode(LinkNode<T> p)
{
next = p;
}
//构造方法只包含数据域的结点
public LinkNode(T val)
{
data = val;
next = null;
}
//构造方法既不包含数据域也不包含引用域的结点
public LinkNode()
{
data = default(T);
next = null;
}
#endregion
#region ----公有属性-----
//数据域属性
public T Data
{
get
{
return data;
}
set
{
data = value;
}
}
//引用域属性
public LinkNode<T> Next
{
get
{
return next;
}
set
{
next = value;
}
}
#endregion
}
}
链栈实体类实现
几个关键的操作:
- 移动链栈的下一节点指针:p=p.Next
- 获取节点的数据:p.Data
- 空链栈:top==null && nodeNum==0
public class LinkSeqStack<T>:IStack<T>
{
#region 成员变量
private LinkNode<T> top;//栈顶指示器
private int nodeNum;//
#endregion
#region 公有属性、索引器
//栈顶指示器属性
public LinkNode<T> Top
{
get
{
return top;
}
set
{
top = value;
}
}
//元素个数属性
public int Num
{
get
{
return nodeNum;
}
set
{
nodeNum = value;
}
}
#endregion
#region 构造方法
public LinkSeqStack()
{
top = null;
nodeNum = 0;
}
#endregion
#region 成员方法
//求链栈的长度即nodeNUm
public int GetLength()
{
return nodeNum;
}
//清空链栈:栈顶指示器置空节且节点数清0
public void Clear()
{
top = null;
nodeNum = 0;
}
//判断链栈是否为空:如果链栈的栈底指示器为null并且num等于0,则链栈为空,返回true,否则返回false
public bool IsEmpty()
{
if ((top == null) && (nodeNum == 0))
{
return true;
}
else
{
return false;
}
}
//链栈的入栈操作在栈顶添加一个新结点,top指向新的结点,num加1,栈发生变化。
public void Push(T item)
{
LinkNode<T> q = new LinkNode<T>(item);
if (top == null)
{
top = q;
}
else
{
q.Next = top;
top = q;
}
++nodeNum;
}
//出栈操作是在栈不为空的情况下,先取出栈顶结点的值,然后将栈顶指示器指向栈顶结点的直接后继结点,使之成为新的栈顶结点,num减1
public T Pop()
{
if (IsEmpty())
{
Console.WriteLine("Stack is empty!");
return default(T);
}
LinkNode<T> p = top;
top = top.Next;
--nodeNum;
return p.Data;
}
//获取链顶结点的值,如果链栈不为空,返回栈顶结点的值,否则返回特殊值表示栈为空,栈不发生变化。
public T GetTop()
{
if (IsEmpty())
{
Console.WriteLine("Stack is empty!");
return default(T);
}
return top.Data;
}
//遍历链栈
public void ShowItem()
{
LinkNode<T> p = top;
while (top != null)
{
Console.Write(top.Data + "\t");
top = top.Next;
}
}
#endregion
}
简单测试:
LinkSeqStack<int> lnkstack = new LinkSeqStack<int>();
lnkstack.Push(32);
lnkstack.Push(92);
//stack.Pop();
Console.WriteLine("遍历链栈:" + "\n");
lnkstack.ShowItem();