(15 C++ Lab) D&A Simple Linked List

(15 C++ Lab) D&A Simple Linked List

标签(空格分隔): 程序设计实验 c++

本人学院


description

Knowledge points: (copy) constructor, deep copy, pointers, dynamic allocation, linked list algorithms, debug methods(GDB or IDE or output debug), memory leak.

In this Lab, you are going to implement a class named list which is a simple version for the list in stl. You are going to use dynamic memory allocation and pointer operations to finish this project.

I recommend you to:

Learn the knowledge points mentioned above.
Use local compilers to test you program rather than submit your answer to the system time after time.
Use local debug tools(GDB is recommended) to debug your code, especially for the memory leak problem. I can tell you that you will meet runtime error problem if you don’t use local debug tools.
Make use of your paper and pen to have some sketches because it’s a good way when you meet list.

Requirements:

Finish all the functions which have been declared inside the hpp file.

Details:

string toString(void) const

Return a visible list using ‘->’ to show the linked relation which is a string like:

1->2->3->4->5->NULL

void insert(int position, const int& data)

Add an element at the given position:

example0:

1->3->4->5->NULL

instert(1, 2);

1->2->3->4->5->NULL

example1:

NULL

insert(0, 1)

1->NULL

void list::erase(int position)

Erase the element at the given position

1->2->3->4->5->NULL

erase(0)

2->3->4->5->NULL

More

Happy coding…

Hint:

建议使用调试工具,注意内存泄露问题,注意在链表头部插入数据的问题,注意.

复习链表:

传送门

加油!么么哒!

MinG 工具(GCC G++ GDB for windows)

请大家使用本地编译器(VS或DEV CPP或MinG)编程调试,不要直接在系统上编写代码!

=============================================================================

请大家相信,这道题是一道很重要的题目,c++里面重要的知识点都在里面体现,特别是c++独有的指针。请不要放弃治疗。

你们骂我也好,吐槽也好,请大家静下心来好好做这道题目,你会有很大收获。

我知道肯定会遇到很多很多问题,希望大家能够平心静气去解决这些问题,比如:

  1. 编译错误,很大一部分同学两节实验课都没能使得程序通过编译,原因就是第一对c++语法很不熟悉,域作用符号与命名空间,函数原型等知识理解有偏差,第二对编译器的报错非常陌生,或者不会看编译器的错误信息,关键是要理解。

  2. 浅拷贝很深拷贝,有同学直接对指针进行赋值,却忽略了指针指向的整个链表。打个比方,浅拷贝就是“创建快捷方式”,深拷贝就是真正的“复制粘贴”,浅拷贝中两个指针指向同一块内容,深拷贝则是两个指针有两块数据相同的内存空间。

3.new delete运算符不会使用,这个可以参考老师课件。

  1. 赋值运算符重载问题,重载时候没有考虑要将当前(this)对象清除导致内存泄露。或者在拷贝构造函数里面调用赋值运算符时候,没有先初始化head和_size。

  2. 链表算法不熟悉,重要的是画图,一定要画图!指针之间怎么解出链接,怎么重新链接的,一定要通过草稿来理解。还有在链表头部操作和空链表是特殊情况,必须特殊处理。

  3. 内存泄露控制,这是一个很关键的知识。解决内存泄露问题:1. 养成良好的编程习惯,在对原指针赋值的时候,时刻想着会不会出现内存块丢失的问题。 2. 可以维护一个容器,通过重载new和delete运算符来记录堆内存的申请 3. 通过eden系统的提示,慢慢寻找内存错误的源头(需要耐心和细心)

如果有问题请找ta,我们很乐意回答你们问题。

请大家多点耐心和细心,静下心来,慢慢调试程序,你所遇到的所有问题都是学习c++路上很珍贵的经验,只有一个一个地解决问题,才能真正提高自己的编程能力。

建议写一个经验总结:

  1. 深拷贝的知识。

  2. 赋值运算符为什么要返回 this对象?

  3. 内存泄露怎么处理?

  4. 编程风格怎么注意?

  5. 链表。

TA出题不易,越难的题目花的时间越多,通常需要写大家3倍的代码(头文件,源文件,测试文件,随机输入),真正能提高你们的编程能力需要你的耐心和细心。

无心虐大家。。。。

harddue改到周日,大家加油。

file provided

main.cpp

#include <iostream>
#include <string>
#include "list.hpp"

using std::cin;
using std::cout;
using std::endl;
using std::string;

int main() {
  list li;

  int n;
  cin >> n;

  for (int i = 0, data, pos; i < n; i++) {
    cin >> pos >> data;
    li.insert(pos, data);
  }

  cout << li.toString() << " size: " << li.size() << endl;

  list li2(li);
  list li3;

  li = li3 = li2 = li;

  cout << li.toString() << " size: " << li.size() << endl;
  cout << li2.toString() << " size: " << li2.size() << endl;
  cout << li3.toString() << " size: " << li3.size() << endl;

  int m;
  cin >> m;

  for (int i = 0, pos; i < m; i++) {
    cin >> pos;
    li.erase(pos);
  }

  cout << li.toString() << endl;

  cout << li.sort().toString() << endl;
  cout << li2.sort().toString() << endl;
  cout << li3.sort().toString() << endl;

  return 0;
}

list.hpp

#ifndef LIST
#define LIST

#include <string>
#include <iostream>

typedef struct node {
  int data;
  struct node* next;
  node(int data = 0, struct node* next = NULL) : data(data), next(next) {}
} node;

class list {
 private:
  node* head;
  int _size;

 public:
  list();
  list(const list&);
  list& operator=(const list&);
  ~list();

  // Capacity
  bool empty(void) const;
  int size(void) const;

 public:
  // output
  // list: [1,2,3,4,5]
  // output: 1->2->3->4->5->NULL
  std::string toString(void) const;

  void insert(int position, const int& data);
  void erase(int position);
  void clear(void) {
    if (this->head != NULL) {
      node* p = this->head;
      while (p != NULL) {
        node* temp = p;
        p = p->next;
        delete temp;
      }
      this->head = NULL;
    }
    this->_size = 0;
  }
  list& sort(void);
};

#endif

understanding

重要理解问题:链表每个元素的序号:0 1 2 3 …

my answer

#include"list.hpp"
#include<iostream>
#include<sstream>
#include <string>

using namespace std;

list::list() {
    head = NULL;
    _size = 0;
}

list::list(const list& other) {
    head = NULL;
    _size = 0;
    node* otherp = other.head;
    for (int i = 0; i < other._size; i++) {
        insert(i, otherp->data);
        otherp = otherp->next;
    }
}

list& list::operator=(const list& other) {
    clear();
    if (other._size != 0) {
        node* otherp = other.head;
        for (int i = 0; i < other._size; i++) {
            insert(i, otherp->data);
            otherp = otherp->next;
        }
    }
    return *this;
}

list::~list() {
    clear();
}

// Capacity
bool list::empty(void) const {
    return (head == NULL);
}

int list::size(void) const {
    return _size;
}


std::string list::toString(void) const {
    stringstream st;
    node* temp = head;
    while (temp != NULL) {
        st << temp->data;  // some problems
        st << "->";
        temp = temp->next;
    }
    st << "NULL";
    return st.str();
}

void list::insert(int position, const int& data) {
    if (position > _size) return;
    if (position == 0) {
        if (head == NULL) {
            head = new node(data, NULL);
        } else {
             node* NewNode = new node(data, head);
             head = NewNode;
        }
        _size++;
        return;
    }
    node* temp = head;
    for (int i = 0; i < position - 1; i++) {
         temp = temp->next;
    }
    node* NewNode = new node;
    NewNode->data = data;
    NewNode->next = temp->next;
    temp->next = NewNode;
    _size++;
}

void list::erase(int position) {
    if (_size == 0 || position > _size - 1) return;

    node* temp = head;
    if (position == 0) {
        head = head->next;
        delete temp;
        temp = NULL;
        _size--;
        return;
    }
    for (int i = 0; i < position - 1; i++) {
         temp = temp->next;
    }
    node* targetnode = temp->next;
    temp->next = targetnode->next;
    delete targetnode;
    targetnode = NULL;
    _size--;
    return;
}

list& list::sort(void) {
    if (head == NULL) return *this;
    node* temp = head;
    int* a = new int[_size];
    for (int i = 0; i < _size; i++) {
        *(a + i) = temp->data;
        temp = temp->next;
    }
    for (int i = 0; i < _size - 1; i++) {
        for (int j = i + 1; j < _size; j++) {
            if (*(a + i) > *(a + j)) {
                int t =  *(a + i);
                *(a + i) = *(a + j);
                *(a + j) = t;
            }
        }
    }
    temp = head;
    for (int i = 0; i < _size; i++) {
        temp->data = *(a + i);
        temp = temp->next;
    }
    delete []a;
    return *this;
}

the standard answer

#include "list.hpp"
#include <string>
#include <sstream>

list::list() {
  this->head = NULL;
  this->_size = 0;
}

list::list(const list& another) {
  this->head = NULL;
  this->_size = 0;
  *(this) = another;
}

list& list::operator=(const list& another) {
  if (this != &another) {
    this->clear();
    if (another.head != NULL) {
      this->head = new node(another.head->data);
      node* p = another.head->next;
      node* q = this->head;
      while (p != NULL) {
        q->next = new node(p->data);
        p = p->next;
        q = q->next;
      }
      q->next = NULL;
    }
    this->_size = another._size;
  }
  return *(this);
}

list::~list() { this->clear(); }

bool list::empty(void) const { return this->_size == 0; }

int list::size(void) const { return this->_size; }

std::string list::toString(void) const {
  node* positioner = this->head;
  std::string result;
  std::stringstream ss;
  while (positioner != NULL) {
    ss << positioner->data;
    std::string temp;
    ss >> temp;
    result += temp + "->";
    ss.clear();
    positioner = positioner->next;
  }
  result += "NULL";
  return result;
}

void list::insert(int position, const int& data) {
  if (position > this->_size || position < 0) {
    return;
  } else if (position == 0) {
    node* temp = new node(data, this->head);
    this->head = temp;
  } else {
    node* p = this->head;
    int counter = 1;
    while (counter != position) {
      p = p->next;
      counter++;
    }
    node* temp = new node(data, p->next);
    p->next = temp;
  }
  this->_size++;
}

void list::erase(int position) {
  if (position >= this->_size || position < 0) {
    return;
  } else if (position == 0) {
    node* temp = this->head;
    this->head = this->head->next;
    delete temp;
  } else {
    node* pre = this->head;
    int counter = 0;
    while (counter != position - 1) {
      counter++;
      pre = pre->next;
    }
    node* temp = pre->next;
    pre->next = temp->next;
    delete temp;
  }
  this->_size--;
}

list& list::sort(void) {
  if (this->head != NULL && this->head->next != NULL) {
    node* slow = head;
    node* fast = head->next;
    while (fast != NULL) {
      if (fast->data >= slow->data) {
        fast = fast->next;
        slow = slow->next;
      } else {
        node* pre = this->head;
        if (this->head->data > fast->data) {
          slow->next = fast->next;
          fast->next = this->head;
          this->head = fast;
        } else {
          while (pre->next->data <= fast->data) {
            pre = pre->next;
          }
          slow->next = fast->next;
          fast->next = pre->next;
          pre->next = fast;
        }
        fast = slow->next;
      }
    }
  }
  return *(this);
}

rethinking

有位同学评论:
顺便看上去还是有挺多人连dev-cpp都不会用,直接在框内打代码,建议开设一节课好好普及一下dev-cpp如何运行这种多文件的project

(File -> New -> Project)

不然一大堆人在eden直接写又卡评测机也严重影响他们的代码效率

至于stringstream和sprintf (snprintf)的应用其实很多人都不是很熟悉

建议还是尽量回避这种使用,或者在eden上面给出一份c++ reference之类的

有问题多查查stackoverflow,多看看 http://www.cplusplus.com/reference/

个人反思:
C++ int与string的转化
其中int转string:
使用stringstream

#include<sstream>

int aa = 30;
stringstream ss;
ss<<aa; 
string s1 = ss.str();
cout<<s1<<endl; // 30

string s2;
ss>>s2;
cout<<s2<<endl; // 30

可以这样理解,stringstream可以吞下不同的类型,根据s2的类型,然后吐出不同的类型。

list();
// 构造函数,主要用于初始化一个list的对象,将head指向NULL,_size置零

list(const list&);
// 复制构造函数.
// 一开始写这个函数,我是先clear();再head = NULL;
// 我想着本来head可能有指向,但实际上,这是构造函数,所以对象为空
// 在构造的时候 head和_size都没有初始化,如果调用clear
// clear会首先判断head是不是NULL,然而这时候head是未定义的
// 我在此处用了insert函数.

list& operator=(const list&);
// 赋值重载需要先把本对象清空
// 在此处用了insert函数.所以一开始_size 应该为0, 后面用size作为判断条件的,都应该用参数对象的_size
// 赋值运算符要返回 this对象

~list();
// 可以直接调用clear

bool empty(void) const;
int size(void) const;

std::string toString(void) const;
// 上面有int转string的方法.
// stringstream
// C++ stringstream介绍,使用方法与例子
// stringstream的用法

void insert(int position, const int& data);
void erase(int position);
// 这两个函数,要注意 1.空链表情况 2.参数不合法(位置超出链表长度) 3.在head位置的插入与去除.

list& sort(void);
// 如果用先放到数组中排好序再重新赋值的方法. 如果数组是用动态内存分配的, 赋值完之后要记得delete

  1. 深拷贝的知识。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
链表翻转是链表操作中的一项基本操作,可以将链表中的节点按照相反的顺序排列。C语言中可以使用指针来实现链表的翻转。 下面是一个简单的链表翻转的例子: ```c #include <stdio.h> #include <stdlib.h> typedef struct node { int data; struct node* next; } Node; Node* reverseList(Node* head) { if (head == NULL || head->next == NULL) { return head; } Node* p = reverseList(head->next); head->next->next = head; head->next = NULL; return p; } void printList(Node* head) { Node* p = head; while (p != NULL) { printf("%d ", p->data); p = p->next; } printf("\n"); } int main() { Node* head = (Node*)malloc(sizeof(Node)); head->data = 1; Node* p1 = (Node*)malloc(sizeof(Node)); p1->data = 2; head->next = p1; Node* p2 = (Node*)malloc(sizeof(Node)); p2->data = 3; p1->next = p2; p2->next = NULL; printf("原链表:"); printList(head); Node* newHead = reverseList(head); printf("翻转后的链表:"); printList(newHead); return 0; } ``` 在上面的代码中,我们定义了一个包含数据和下一个节点指针的结构体Node,然后我们定义了一个reverseList函数,该函数使用递归来翻转链表。在翻转链表的过程中,我们首先递归遍历链表,然后将当前节点的下一个节点的next指针指向当前节点,最后将当前节点的next指针设为NULL,以防止链表出现环。最后返回递归遍历的第一个节点,也就是翻转后的链表的头节点。 最后在main函数中,我们创建了一个包含3个节点的链表,然后打印出原链表和翻转后的链表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值