这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战
栈的链式存储结构,简称为链栈。
想想看,栈只是栈顶来做插入和删除操作,栈顶放在链表的头部还是尾部呢?
由于单链表有头指针,而栈顶指针也是必须的,那么干嘛不让他们合二为一呢,所以比较好的办法是把栈顶放到单链表的头部。
另外栈顶在头部了,那么单链表的头结点也就失去了意义,通常对于链栈来说,是不需要头结点的。
同样对于链栈来说,基本不存在栈满的情况,除非内存已经没有可用的空间了。
栈的链式存储结构与线性表的链式存储结构差不多,但是其相对比较简单,只在表尾进行操作。
刚好复习下之前学习的线性表中的链表。
但是链栈和线性表的链式存储结构是有区别的,链栈不需要头结点,有栈顶就可以了。
首先定义一个链栈的接口:
/// <summary>
/// 链栈接口
/// </summary>
public interface IChainStack<T>
{
/// <summary>
/// 清空栈
/// </summary>
/// <returns></returns>
bool ClearStack();
/// <summary>
/// 判断栈是否为空
/// </summary>
/// <returns>true:空、false:不空</returns>
bool IsEmptyStack();
/// <summary>
/// 获取栈顶元素
/// </summary>
/// <returns></returns>
T GetTopStack();
/// <summary>
/// 入栈
/// </summary>
/// <returns></returns>
bool PushStack(T val);
/// <summary>
/// 出栈
/// </summary>
/// <returns></returns>
bool PopStack();
/// <summary>
/// 获取栈长度
/// </summary>
/// <returns></returns>
int LengthStack();
/// <summary>
/// 判断栈是否已满
/// </summary>
/// <returns></returns>
bool IsFull();
/// <summary>
/// 打印栈
/// </summary>
void paint();
}
然后定义一个链栈的节点
/// <summary>
/// 链栈节点类
/// </summary>
public class ChainStackNode<T>
{
/// <summary>
/// 链表中的数据域
/// </summary>
public T data;
/// <summary>
/// 链表中的下一个节点的指针域
/// </summary>
public ChainStackNode<T> Next;
// 每个结点有两部分组成,指针+数据,第一个节点没有数据,只有指针,最后一个节点只有数据,没有指针。
// 那么构造函数需要有三次重载。
/// <summary>
/// 中间节点构造函数
/// </summary>
/// <param name="val"></param>
/// <param name="index"></param>
public ChainStackNode(T val, ChainStackNode<T> index)
{
this.data = val;
this.Next = index;
}
/// <summary>
/// 最后一个节点构造函数
/// </summary>
/// <param name="val"></param>
public ChainStackNode(T val)
{
this.data = val;
this.Next = null;
}
/// <summary>
/// 第一个节点构造函数
/// </summary>
/// <param name="val"></param>
/// <param name="index"></param>
public ChainStackNode(ChainStackNode<T> index)
{
// 默认值
this.data = default(T);
this.Next = index;
}
/// <summary>
/// 定义一个空节点
/// </summary>
/// <param name="val"></param>
/// <param name="index"></param>
public ChainStackNode()
{
this.data = default(T);
this.Next = null;
}
}
最后我们定义一个类来实现上边定义的接口;实现链栈的操作功能。
public class ChainStackList<T> : IChainStack<T>
{
/// <summary>
/// 存储栈顶指针,其是有指针,没有数据
/// </summary>
public ChainStackNode<T> top = null;
/// <summary>
/// 栈数量
/// </summary>
public int count = 0;
public ChainStackList()
{
top = new ChainStackNode<T>();
count = 0;
}
/// <summary>
/// 清空栈
/// </summary>
/// <returns></returns>
public bool ClearStack()
{
count = 0;
top = null;
return true;
}
/// <summary>
/// 获取栈顶数据
/// </summary>
/// <returns></returns>
public T GetTopStack()
{
return top.Next.data;
}
/// <summary>
/// 栈是否为空
/// </summary>
/// <returns></returns>
public bool IsEmptyStack()
{
if (top.Next == null || count == 0)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 栈是已满,链接基本上不存在满栈的情况,因为满栈基本上意味着系统崩溃
/// </summary>
/// <returns></returns>
public bool IsFull()
{
if (top == null)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 获取栈长度
/// </summary>
/// <returns></returns>
public int LengthStack()
{
return count;
}
/// <summary>
/// 打印栈
/// </summary>
public void paint()
{
ChainStackNode<T> tempTop = top;
ChainStackNode<T> temp = new ChainStackNode<T>();
for (int i = 0; i < count+1; i++)
{
temp = tempTop.Next;
Console.WriteLine("data:"+ tempTop.data+"index:"+top.Next);
tempTop = temp;
}
}
/// <summary>
/// 出栈
/// </summary>
/// <returns></returns>
public bool PopStack()
{
if (IsEmptyStack())
{
Console.WriteLine("链栈为空,无法弹出");
return false;
}
// 定义一个节点存储栈顶的下一个节点
ChainStackNode<T> temp = top.Next;
// 现在栈顶的下一个节点等于原来栈顶下一个节点的先一个节点
top.Next = temp.Next;
count--;
return true;
}
/// <summary>
/// 入栈
/// </summary>
/// <param name="val">入栈数据</param>
/// <returns></returns>
public bool PushStack(T val)
{
ChainStackNode<T> temp = new ChainStackNode<T>(val);
if (temp == null)
{
Console.WriteLine("栈已满");
return false;
}
// 将栈顶的下一个节点赋值给新节点
temp.Next = top.Next;
// 将新节点赋值给栈顶的下一个值
top.Next = temp;
count++;
return true;
}
}
调用示例:
static void Main(string[] args)
{
ChainStackList<string> chainStrackList = new ChainStackList<string>();
chainStrackList.PushStack("11111");
chainStrackList.PushStack("22222");
chainStrackList.PushStack("33333");
chainStrackList.PushStack("44444");
chainStrackList.PushStack("55555");
Console.WriteLine("添加数据");
Console.WriteLine("=========================================");
Console.WriteLine("打印数据");
chainStrackList.paint();
Console.WriteLine("=========================================");
chainStrackList.PopStack();
chainStrackList.PopStack();
Console.WriteLine("弹出栈");
Console.WriteLine("=========================================");
Console.WriteLine("打印数据");
chainStrackList.paint();
Console.WriteLine("=========================================");
Console.WriteLine("获取栈顶数据:"+ chainStrackList.GetTopStack());
Console.WriteLine("=========================================");
Console.WriteLine("获取栈的长度:" + chainStrackList.LengthStack());
Console.WriteLine("=========================================");
Console.ReadKey();
}
如上图所示:
在写链栈的时候,只需要搞清楚一件事情就可以了,栈顶top存储的是事实上的栈顶的指针。
Top.Next = 真正的栈顶。
那么添加数据时:
newNode.Next = top.Next;// 将栈顶的下一个节点赋值给新节点
top.Next = newNode;// 将新节点赋值给栈顶的下一个节点
删除数据时:
Temp = top.Next;// 定义一个节点存储栈顶的下一个节点
Top.next = temp.next;// 现在栈顶的下一个节点等于原来栈顶下一个节点的先一个节点
以上基本上就是链栈的精髓所在,最后放上一张上边代码最终运行的效果图:代码实例在文末,可下载。
有好的建议,请在下方输入你的评论。
欢迎访问个人博客 guanchao.site
欢迎访问小程序: