Split doubly linked list


I have got a doubly linked list
and pointer to the node
How can I write splitAfter and splitBefore functions
Functions have to include border cases

These functions would be helpful when i
play with divide and conquer algorithms on linked list

For example splitting node for recursive merge sort can be found in this way

1
2
3
4
5
6
7
8
p = L.head;
q = L.tail;
while (p != q && p->next != q )
{
     p = p->next;
     q = q->prev;
}
return p;


Here we need splitAfter function

1
2
3
4
5
6
7
8
p = L.head;
q = L.head;
while(q != NULL && q->next != NULL)
{
      p = p->next;
      q = q->next->next;
}
return p;


Here we need splitBefore function

Last edited on
The standard library has a doubly linked list https://en.cppreference.com/w/cpp/container/list
It supports splicing; https://en.cppreference.com/w/cpp/container/list/splice
split can be implemented in terms of splice.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <list>
#include <string>
#include <algorithm>

template < typename T, typename A >
std::list<T,A> split_before( std::list<T,A>& lst, typename std::list<T,A>::const_iterator here )
{
    std::list<T,A> second_list ;

    // https://en.cppreference.com/w/cpp/container/list/splice
    second_list.splice( second_list.begin(), lst, here, lst.end() ) ;

    return second_list ;
}

template < typename T, typename A >
std::list<T,A> split_after( std::list<T,A>& lst, typename std::list<T,A>::const_iterator here )
{
    if( here == lst.end() ) return {} ; // return empty list
    else return split_before( lst, ++here ) ;
}

template < typename SEQ > void print( const SEQ& seq )
{
    std::cout << "[ " ;
    for( const auto& v : seq ) std::cout << v << ' ' ;
    std::cout << "]\n" ;
}

int main()
{
    std::list<std::string> a = { "Church", "Turing", "Ada", "Knuth", "Wirth", "Dijkstra",
                                 "Neumann", "Kleene", "Minsky", "Ritchie", "Thomson", "Stroustrup" } ;
    print(a) ;

    std::cout << "\nsplit after Dijkstra:\n" ;
    auto b = split_after( a, std::find( a.begin(), a.end(), "Dijkstra" ) ) ;
    print(a) ;
    print(b) ;

    std::cout << "\nsplit second list before Ritchie:\n" ;
    const auto c = split_before( b, std::find( b.begin(), b.end(), "Ritchie" ) ) ;
    print(a) ;
    print(b) ;
    print(c) ;
}

http://coliru.stacked-crooked.com/a/a6253ec87cee617b
Yes but ready solutions from STL dont show how it works
I will need these functions to play with divide and conquer functions

Hindu guy from youtube wrote find_middle function and merge function
His middle node is in fact head of second linked list so we need splitBefore
If we write our own find middle function we will need splitAfter function
pointer manipulations.
to split before a node,
head2 = node; //new list starts here. //this may be several assignment statements, eg next/prev
oldlistcurrent -> next = null; //chop off before node in original list. assuming ->next is node in question.

to split after a node: call the above on node->next ;)

that is, I am assuming if list is 1,2,3,4,5 and node is 3:
split before is
list 1: 1,2
list 2: 3,4,5
and split after is
list 1: 1,2,3
list 2: 4,5

be sure to check nulls and set nulls and such. new list -> prev is null @ new head. old list tail -> next is null. right?
Last edited on
Definitely implement split_after() by using split_before().

If you can't keep the points straight in your head, draw them out on a piece of paper. Use one color pen to draw the pointers before the split and a different code to draw the pointers after the split.
Dave your Concat function was really helpful for partition sort
Idea for partition i saw in Gonnet & Baeza handbook but in the internet
there is Goodrich and Tamassia slides with the same concept for partition

Can i use split_before() to implement split_after()
What about base cases are they the same ?


split_before() and split_after() are as useful for merge_sort() as Concat()
for partition_sort()

Last edited on
you can use either before or after, write one and you can use it to make the other one.

all you have to check is that the input pointer is not null, so you can dereference it to pass that to the other one.
eg, if you did before first, then after is splitbefore(input->next) so input better not be null. If it is, just return null. similar if you do after first.

Note that the one you wrote first will have additional pointer / edge case checks.
Last edited on

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include<iostream>
#include<cstdlib>
#include<ctime>

struct Node
{
    int key;
    struct Node *prev;
    struct Node *next;
};

struct List
{
    struct Node *head;
    struct Node *tail;
};

void ListInit(struct List &L)
{
    L.head = nullptr;
    L.tail = nullptr;
}

bool ListIsEmpty(struct List L)
{
    return (L.head == nullptr && L.tail == nullptr);
}

Node *ListSearch(struct List L,int k)
{
    Node *x = L.head;
    while(x != nullptr && x->key != k)
        x = x->next;
    return x;
}

void ListInsert(struct List &L,int k)
{
    Node *x = new Node;
    x->key = k;
    x->next = L.head;
    if(L.head != nullptr)
        L.head->prev = x;
    L.head = x;
    x->prev = nullptr;
    if(x->next == nullptr)
        L.tail = x;
}

void ListDelete(struct List &L,int k)
{
    Node *x = ListSearch(L,k);
    if(x != nullptr)
    {
        if(x->next == nullptr)
            L.tail = x->prev;
        if(x->prev != nullptr)
            x->prev->next = x->next;
        else
            L.head = x->next;
        if(x->next != nullptr)
            x->next->prev = x->prev;
        delete x;
    }
}

void ListDispayForward(struct List L)
{
    int counter = 0;
    struct Node *p = L.head;
    while(p != nullptr)
    {
        std::cout<<p->key<<" -> ";
        p = p->next;
        counter++;
    }
    std::cout<<"NULL \n";
    std::cout<<"Liczba wezlow listy : "<< counter <<'\n';
}

void ListDispayBackward(struct List L)
{
    int counter = 0;
    struct Node *p = L.tail;
    while(p != nullptr)
    {
        std::cout<<p->key<<" -> ";
        p = p->prev;
        counter++;
    }
    std::cout<<"NULL \n";
    std::cout<<"Liczba wezlow listy : "<< counter <<'\n';
}



void ListSplitAfter(struct Node *x,struct List &L,struct List &L1,struct List &L2)
{
    struct Node *nextX;
    if(x == nullptr || x->next == nullptr)
    {
        L1.head = L.head;
        L1.tail = L.tail;
        L2.head = nullptr;
        L2.tail = nullptr;
    }
    else
    {
        nextX = x->next;
        x->next = nullptr;
        nextX->prev = nullptr;
        L1.head = L.head;
        L1.tail = x;
        L2.head = nextX;
        L2.tail = L.tail;
    }
    ListInit(L);
}


void ListSplitBefore(struct Node *x,struct List &L,struct List &L1,struct List &L2)
{
    struct Node *prevX;
    if(x == nullptr || x->prev == nullptr)
    {
        L1.head = nullptr;
        L1.tail = nullptr;
        L2.head = L.head;
        L2.tail = L.tail;
    }
    else
    {
        prevX = x->prev;
        x->prev = nullptr;
        prevX->next = nullptr;
        L1.head = L.head;
        L1.tail = prevX;
        L2.head = x;
        L2.tail = L.tail;
    }
    ListInit(L);
}

int main()
{
    int k,n,m,p;
    List L,L1,L2;
    Node *x;
    ListInit(L);
    ListInit(L1);
    ListInit(L2);
    srand(time(nullptr));
        std::cout<<"Ile liczb wylosowac \n";
        std::cin>>n;
        std::cout<<"Podaj gorny zakres przedzialu dla losowanych liczb \n";
        std::cin>>m;
        for(k = 1;k <= n;k++)
            ListInsert(L,rand()%m);
        std::cout<<"Lista L \n";
        ListDispayForward(L);
        ListDispayBackward(L);
        std::cout<<"Podaj element za ktorym chcesz rozdzielic liste \n";
        std::cin>>p;
        x = ListSearch(L,p);
        ListSplitBefore(x,L,L1,L2);
        std::cout<<"Lista L1 \n";
        ListDispayForward(L1);
        ListDispayBackward(L1);
        std::cout<<"Lista L2 \n";
        ListDispayForward(L2);
        ListDispayBackward(L2);
        std::cout<<"Lista L \n";
        ListDispayForward(L);
        ListDispayBackward(L);
        while(!ListIsEmpty(L1))
           ListDelete(L1,L1.head->key);
        while(!ListIsEmpty(L2))
           ListDelete(L2,L2.head->key);
        std::cout<<"Lista L1 \n";
        ListDispayForward(L1);
        ListDispayBackward(L1);
        std::cout<<"Lista L2 \n";
        ListDispayForward(L2);
        ListDispayBackward(L2);
       std::cout<<"Lista L \n";
        ListDispayForward(L);
        ListDispayBackward(L);
        system("PAUSE");
    return 0;
}


Can you simplify or correct this code ?

Give List.head and List.tail initial values.

The ListXYZ() functions should all be methods of struct List where the parameter L becomes the "this" pointer.

Empty() doesn't need to check both head and tail. If one is null then the other should be also.

Use for loops when the loop has initialize, test, increment semantics, such as in ListSearch()

In ListInsert, rather than checking for x->next == nullptr, just put an else in the if(L.head) part

if (ptr != nullptr) is exactly equivalent to if (ptr)

Implement ListSplitBefore using ListSplitAfter
Use the assignment operator and ListInit in the Split routine.

Putting it all together:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include<iostream>
#include<cstdlib>
#include<ctime>

struct Node
{
    int key;
    struct Node *prev;
    struct Node *next;
};

struct List
{
    struct Node *head = nullptr;
    struct Node *tail = nullptr;
    void Init();
    bool IsEmpty() { return head == nullptr; }
    Node *Search(int k);
    void Insert(int k);		// insert at head.
    void Delete(int k);
    void DisplayForward();
    void DisplayBackward();
    void SplitAfter(Node *where, List &L1, List &L2);
    void SplitBefore(Node *where, List &L1, List &L2);
};
    
void List :: Init()
{
    head = tail = nullptr;
}


Node *
List::Search(int k)
{
    for (Node *x = head; x; x = x->next) {
	if (x->key == k) return x;
    }
    return nullptr;
}

// insert at head
void
List::Insert(int k)
{
    Node *x = new Node;
    x->key = k;
    x->next = head;
    if (head) {
	head->prev = x;
    } else {
	tail = x;
    }
    head = x;
    x->prev = nullptr;
}

void
List::Delete(int k)
{
    Node *x = Search(k);
    if (x) {
	if (x->next == nullptr)
	    tail = x->prev;
	else
	    x->next->prev = x->prev;
	if (x->prev)
	    x->prev->next = x->next;
	else
	    head = x->next;
	delete x;
    }
}

void
List::DisplayForward()
{
    int counter = 0;
    for (struct Node *p = head; p; p = p->next) {
	std::cout << p->key << " -> ";
	counter++;
    }
    std::cout << "NULL \n";
    std::cout << "Liczba wezlow listy : " << counter << '\n';
}

void
List::DisplayBackward()
{
    int counter = 0;
    for (struct Node *p = tail; p; p = p->prev) {
	std::cout << p->key << " -> ";
	counter++;
    }
    std::cout << "NULL \n";
    std::cout << "Liczba wezlow listy : " << counter << '\n';
}

// Split after x. If x==nullptr, L1 gets the entire list
void
List::SplitAfter(struct Node *x, struct List &L1, struct List &L2)
{
    struct Node *nextX;
    if (x == nullptr || x->next == nullptr) {
	L1 = *this;
	L2.Init();
    } else {
	nextX = x->next;
	x->next = nullptr;
	nextX->prev = nullptr;
	L1.head = head;
	L1.tail = x;
	L2.head = nextX;
	L2.tail = tail;
    }
    Init();
}

// Split before x. If x == nullptr, L2 gets the entire list
void
List::SplitBefore(struct Node *x, struct List &L1, struct List &L2)
{
    if (x == nullptr) {
	L1.Init();
	L2 = *this;
	Init();
    } else {
	SplitAfter(x->next, L1, L2);
    }
}

int
main()
{
    int k, n, m, p;
    List L, L1, L2;
    Node *x;
    srand(time(nullptr));
    std::cout << "Ile liczb wylosowac \n";
    std::cin >> n;
    std::cout << "Podaj gorny zakres przedzialu dla losowanych liczb \n";
    std::cin >> m;
    for (k = 1; k <= n; k++)
	L.Insert(rand() % m);
    std::cout << "Lista L \n";
    L.DisplayForward();
    L.DisplayBackward();
    std::cout << "Podaj element za ktorym chcesz rozdzielic liste \n";
    std::cin >> p;
    x = L.Search(p);
    L.SplitBefore(x, L1, L2);
    std::cout << "Lista L1 \n";
    L1.DisplayForward();
    L1.DisplayBackward();
    std::cout << "Lista L2 \n";
    L2.DisplayForward();
    L2.DisplayBackward();
    std::cout << "Lista L \n";
    L.DisplayForward();
    L.DisplayBackward();
    while (!L1.IsEmpty())
	L1.Delete(L1.head->key);
    while (!L2.IsEmpty())
	L2.Delete(L2.head->key);
    std::cout << "Lista L1 \n";
    L1.DisplayForward();
    L1.DisplayBackward();
    std::cout << "Lista L2 \n";
    L2.DisplayForward();
    L2.DisplayBackward();
    std::cout << "Lista L \n";
    L.DisplayForward();
    L.DisplayBackward();
    system("PAUSE");
    return 0;
}
Topic archived. No new replies allowed.