数据结构与算法学习cpp(一):链表
1、哨兵节点
带头链表中,不存储任何数据的头节点就是一种哨兵节点,可以简化普通单链表插入、删除代码。
1.1 不带头链表操作:
1、插入:
if (head == null)//当前链表为空
{
head = new_node;
}
else
{
new_node->next = p->next;
p->next = new_node;
}
2、删除:
if (head->next == null) //当前为最后一个元素
{
head = null;
}
else
{
p->next = p->next->next;
}
1.2 带头链表操作:
若当前链表为空,则head->next
为NULL
,插入操作为:
new_node->next = head->next;//NULL
head->next = new_node;
若当前仅剩最后一个元素,则该元素为head->next
,此时head->next->next
为NULL
,删除操作为:
head->next = head->next->next;//NULL
可以看出,在任何时候,不管链表是不是空,head 指针都会一直指向这个哨兵结点,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现。
除了链表外,平时代码中也可以使用哨兵节点的方式来简化判断逻辑。
2、链表操作
一句废话:平时工作似乎curd或直接用容器就可,但是写这些东西可以提高代码能力。
2.1 链表类:
struct ListNode
{
int data; //数据
ListNode* next; //指向下一节点指针
ListNode()
{
data = NULL;
next = nullptr;
}
};
//链表类
class LinkList
{
public:
LinkList(); //构造
~LinkList(); //析构
void print(); //打印链表元素
inline int getLength()
{
return length;
};
void pushHead(int data); //头插
void pushBack(int data); //尾插
void insert(int data, int position); //插入指定位置
void remove(int position); //删除指定位置元素
ListNode* findNode(int position); //查找第position个元素
void reverse(); //链表反转
private:
ListNode* head;
int length;
};
2.2 构造(新建)、析构(删除):
LinkList::LinkList()
{
head = new ListNode();
head->next = nullptr;
head->data = NULL;
length = 0;
}
LinkList::~LinkList()
{
if (length == 0)
{
delete head; //删除头节点空间
head = nullptr;
return;
}
ListNode* p = head->next;
delete head;
while(p != nullptr)
{
ListNode* tmpptr = p->next;
delete p;
p = tmpptr;
}
length = 0;
}
2.3 打印(遍历节点):
void LinkList::print()
{
if (length == 0) return;
ListNode* temp = head->next;
cout <<"current list:"<< endl;
int i;
for (i = 0; i < length; i++)
{
cout << setw(4) <<temp->data;
temp = temp->next;
}
cout << endl << endl;
temp = nullptr;
}
2.4 定位节点:
ListNode* LinkList::findNode(int position)
{
ListNode* temp = new ListNode();
if (position > length+1)
{
cout<<"position"<<position<<"out of range!" << endl;
temp->data = NULL;
temp->next = nullptr;
return temp;
}
temp = head;
while (position--)
{
temp = temp->next;
}
return temp;
}
2.5 头插:
头插示例:
head->1->null;
插入2:head->2->1->null;
插入3:head->3->2->1->null;
…
void LinkList::pushHead(int data) //头插
{
ListNode* cur = new ListNode();
cur->data = data;
cur->next = head->next;
head->next = cur;
cur = nullptr;
length++;
return;
}
2.6 尾插:
尾插示例:
head->1->null;
插入2:head->1->2->null;
插入3:head->1->2->3->null;
…
void LinkList::pushBack(int data) //尾插
{
ListNode* cur = new ListNode();
cur->data = data;
cur->next = nullptr;
ListNode* pre = findNode(length); //找到当前最后一节点
pre->next = cur;
length++;
return;
}
2.7 指定位置插:
void LinkList::insert(int data, int position)//指定位置插
{
if (position > length+1 || position < 1)
{
cout << "position" << position << "out of range" << endl;
cout << endl;
return;
}
if (position == length+1)
{
pushBack(data);
}
ListNode* pre = findNode(position-1);
ListNode* cur = new ListNode();
cur->data = data;
cur->next = pre->next;
pre->next = cur;
length++;
return;
}
2.7 删除指定节点:
找到待删除节点的前一个结点执行操作,别忘记释放空间。
void LinkList::remove(int position)
{
if (position > length + 1 || position < 1)
{
cout << "position" << position << "out of range" << endl;
cout << endl;
return;
}
ListNode* pre = findNode(position - 1);//找到前节点
ListNode* cur = pre->next;
pre->next = cur->next;
delete cur;
length--;
}
2.8 反转链表:
反转示例:
head->1->2->3->4->5->null;
反转为:
head->5->4->3->2->1->null
void LinkList::reverse()
{
if (length < 2)
{
return;
}
ListNode* pre = head->next;
ListNode* cur = pre->next;
while (cur)
{
ListNode* next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
head->next->next = nullptr;
head->next = pre;
}
反转链表较基础操作稍微复杂些,在此作简要说明。
思路:
使用两个指针ListNode* pre
与ListNode* cur
,要做的就是将原链表pre->cur
方向,更改为cur->pre
,从第一个节点开始,没变换一次方向就将两个指针向后移动一位,一直迭代到链表结束;
图示:
2.9 测试及结果:
int main()
{
LinkList Testlist;
int i = 1;
for (i = 1; i < 6; i++)
{
Testlist.pushBack(i);
}
Testlist.print();
Testlist.insert(6,3);
Testlist.print();
Testlist.reverse();
Testlist.print();
return 0;
}
结果:
gitee:cpp代码(vs2019可运行)